How do I unit-test a dynamic library using gcov? - c

I am building a dynamically-linked library, and am setting up a simple test suite. I want to use gcov to generate a static code analysis coverage report.
My library is a C file containing function implementations and a header file containing function prototypes. My test suite is simply an application that calls the functions in various ways and confirms the validity of the output.
I am compiling both the library and the test suite with the -fprofile-arcs and -ftest-coverage flags, as described on GNU's guide to GCOV. I am also including the -O0 flag to disable compiler optimization and the -g flag to enable debug symbols. The executable generated from the test suite is linked dynamically to the library.
All files compile cleanly and without warning, but it fails to link the test suite to the library -- citing a "hidden symbol __gcov_merge_add". If I compile without the -fprofile-arcs and -ftest-coverage flags, the linking succeeds and I am able to run the test suite executable.
So I have a few questions which still aren't resolved after reading the GNU guide to GCOV.
Why is the linking failing? How can I resolve this?
Do I need to include the profile and coverage flags when compiling both the library and the test suite?
Here is my inc/mylib.h file:
#ifndef __MYLIB_H__
#define __MYLIB_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int
foo (int a);
int
bar (int a);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __MYLIB_H__ */
Here is my src/mylib.c file:
#include <stdio.h>
#include "mylib.h"
int foo(int a) {
if (a > 5) {
return 5;
}
return a;
}
int bar(int a) {
if (a < 0) {
return 0;
}
return a;
}
Here is my test/unittests.c file:
#include <stdio.h>
#include "mylib.h"
void run_foo_tests() {
int inputs[] = {3, 6};
int expected_results[] = {3, 5};
int i, actual_result;
for ( i = 0; i < sizeof(inputs) / sizeof(int); i++ ) {
actual_result = foo(inputs[i]);
if (actual_result == expected_results[i]) {
printf("Test %d passed!\n", i + 1);
} else {
printf("Test %d failed!\n", i + 1);
printf(" Expected result: %d\n", expected_results[i]);
printf(" Actual result: %d\n", actual_result);
}
}
}
void run_bar_tests() {
int inputs[] = {3, -1};
int expected_results[] = {3, 0};
int i, actual_result;
for ( i = 0; i < sizeof(inputs) / sizeof(int); i++ ) {
actual_result = bar(inputs[i]);
if (actual_result == expected_results[i]) {
printf("Test %d passed!\n", i + 1);
} else {
printf("Test %d failed!\n", i + 1);
printf(" Expected result: %d\n", expected_results[i]);
printf(" Actual result: %d\n", actual_result);
}
}
}
int main(int argc, char *argv[]) {
run_foo_tests();
run_bar_tests();
return 0;
}
Here is my Makefile:
CC=gcc
CFLAGS=-Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage
all: clean build run_tests
build:
$(CC) $(CFLAGS) -fPIC -c src/*.c -o lib/mylib.o
$(CC) -shared lib/mylib.o -o lib/libmylib.so
$(CC) $(CFLAGS) test/*.c -o bin/unittests -Llib -lmylib
run_tests:
LD_LIBRARY_PATH=lib bin/unittests
gcov src/*.c
clean:
rm -f *.gcda *.gcno *.gcov
rm -rf bin lib ; mkdir bin lib
When I run make, I am presented with this output:
rm -f *.gcda *.gcno *.gcov
rm -rf bin lib ; mkdir bin lib
gcc -Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage -fPIC -c src/*.c -o lib/mylib.o
gcc -shared lib/mylib.o -o lib/libmylib.so
gcc -Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage test/*.c -o bin/unittests -Llib -lmylib
/usr/bin/ld: bin/unittests: hidden symbol `__gcov_merge_add' in /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcov.a(_gcov_merge_add.o) is referenced by DSO
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
make: *** [build] Error 1

You need to give the command line argument -lgcov for the linker (normally -ftest-coverage implies -lgcov, but you have a separate linking step where -ftest-coverage is not given as a command line argument). Alternatively, you can just use the --coverage command line argument, which is a shortcut also for -fprofile-arcs and -ftest-coverage, as explained here: http://www.univ-orleans.fr/sciences/info/ressources/webada/doc/gnat/gcc_3.html:
--coverage
This option is used to compile and link code instrumented for
coverage analysis. The option is a synonym for `-fprofile-arcs'
`-ftest-coverage' (when compiling) and `-lgcov' (when linking). See
the documentation for those options for more details.
At the same place, btw., it is also explained that you don't have to compile all files with these options, which hopefully answers your question 2.

Related

Compile time error while running multiple c files

#Edited
I am tested on simple two files now that are add.h and add.c.
I made a Makefile in order to compile my program. Here is my makefile.
# Make file for running the project
CC=gcc
CFLAGS= -Wall -g
LDFLAGS = -include
OBJFILES = add.o
LIB = add.h
TARGET = add
all: ${TARGET}
%.o: %.c
${CC} ${CFLAGS} -c -o $# $<
${TARGET}: ${OBJFILES}
${CC} ${CFLAGS} -o ${TARGET} ${OBJFILES}
clean:
rm -f $(OBJFILES) $(TARGET) *~
when I run
make add
I get the following error:
gcc -Wall -g -c -o add.o add.c
gcc -Wall -g -o add add.o
Undefined symbols for architecture x86_64:
"_b", referenced from:
_main in add.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [add] Error 1
here are snippets of my code
add.c
#include <stdio.h>
#include <stdlib.h>
#include "add.h"
int main(void) {
int a = 10, b=20;
add(a, b);
return 0;
}
add.h
#ifndef __ADD_H_
#define __ADD_H_
extern int a,b;
int add(a,b)
{
return a+b;
}
#endif // __ADD_H_
You are lacking a rule to actually build your object files. Right now you only have one to link them all together once they already exist. Try adding:
%.o: %.c
${CC} ${CFLAGS} -c -o $# $<
This tells make how to build object files out of source files.
You are misusing the extern keyword, losing track of your variables and making your code too complicated.
Let's try something very simple:
int main(void) {
int a = 10; b=20;
return 0;
}
This fails. The compiler complains about the statement b=20;, since it has never heard of this b. The semicolon that made this a separate statement was either a typo or a conceptual error caused by declaring extern int b elsewhere. There is no need for extern here, at least not yet.
This:
int main(void) {
int a=10, b=20;
return 0;
}
works.
Now for an add function.
int add(int a, int b)
{
return a+b;
}
int main(void) {
int a = 10, b=20;
add(a,b);
return 0;
}
Note that the a and b in add are not the same variables as the a and b in main. This is crucial; do not proceed until you understand it.
Now add a declaration of the add function:
int add(int a, int b); // <- declaration
int add(int a, int b) // <- definition
{
return a+b;
}
The declaration can be moved into a header file (add.h); the definition belongs in a source file (add.c).
Finally, I would advise you to add a line to the makefile:
add.o: add.h
Aren't you missing an #endif at the end of the last header file queue.h ?
This can be for sure an issue when compiling. Moreover, aren't you missing the main_application.h header file you are then including within the stack.h one for example?
Have a look at this: https://medium.com/#m.muizzsuddin_25037/error-ld-symbol-not-found-for-architecture-x86-64-a5e5b648ffc seems helpful here and point our attention to the header files again!..

C - multiple definition with makefile

I am trying to compile my C program using make and I've come across this problem that I can't quite understand. I have 3 files in the 'calc' folder of my project: add.c sub.c and main.c. I have my Makefile located in the root folder of my project, which has the calc folder that I mentioned in it. This is what my Makefile looks like:
CC=gcc
OBJECTS=obj/main.o obj/add.o obj/sub.o
elf/new: ${OBJECTS}
${CC} -o elf/new ${OBJECTS}
obj/main.o: calc/main.c
${CC} -c -g calc/main.c -o obj/main.o
obj/add.o: calc/add.c
${CC} -c -g calc/add.c -o obj/add.o
obj/sub.o: calc/sub.c
${CC} -c -g calc/sub.c -o obj/sub.o
clean:
rm obj/${OBJECTS} elf/new
When I type 'make' into the terminal to compile, I get an error like this:
gcc -c -g calc/add.c -o obj/add.o
gcc -c -g calc/sub.c -o obj/sub.o
gcc -o elf/new obj/main.o obj/add.o obj/sub.o
obj/add.o: In function `add':
/home/bigger/workspace/test/calc/add.c:1: multiple definition of `add'
obj/main.o:/home/bigger/workspace/test/calc/add.c:1: first defined here
obj/sub.o: In function `sub':
/home/bigger/workspace/test/calc/sub.c:1: multiple definition of `sub'
obj/main.o:/home/bigger/workspace/test/calc/sub.c:1: first defined here
collect2: error: ld returned 1 exit status
makefile:5: recipe for target 'elf/new' failed
make: *** [elf/new] Error 1
And my code are there:
bigger#linux:~/workspace/test> cat calc/add.c
int add(int a, int b){
return a+b;
}
bigger#linux:~/workspace/test> cat calc/sub.c
int sub(int a, int b) {
return a-b;
}
bigger#linux:~/workspace/test> cat calc/main.c
#include <stdio.h>
#include "add.c"
#include "sub.c"
int main(int argc, char* argv[])
{
int a = 10;
int b = 5;
printf("add: %d\nsub:%d\n", a+b, a-b);
return 0;
}
When you include it is making the functions add and sub part of your main.c, then when you make you are linking main (which already has the functions by include) to the add and sub objects which have the same function symbols. You need to include header files with function declarations rather than include function definitions. See http://www.cprogramming.com/declare_vs_define.html for a longer discussion.

Dynamic library dynamic attach

EDIT found an issue, but it still needs to be solved it should be below in answers
My task is to write app based on existing files. test.c(main) randapi.c randapi.h(2 functions in here) and initapi.c(one function). "how can You use dynamic library as dynamic loaded library. Using eg9 (where i made a dynamic library and it worked fine) write app where this libraries will be attached dynamic"
here is my try with a makefile but terminal says that :failed to open when i come to run file using ./program
i have tried also version without attaching initapi.c but then it says initRand is unknown besides that make file clearly attached it
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#define ITERATIONS 1000000L
int main(int argc, char** argv)
{
long i;
long isum;
float fsum;
void *lib;
lib=dlopen("librandapi.so", RTLD_LAZY);
if (!lib)
{
printf("failed to open");
exit(1);
}
int (*getRand)(int);
float (*getSRand)();
void (*initRand)();
getRand=dlsym(lib,"getRand");
getSRand=dlsym(lib,"getSRand");
initRand=dlsym(lib,"initRand");
initRand();
isum = 0L;
for (i = 0 ; i < ITERATIONS ; i++) {
isum += ((*getRand)(10));
}
printf( "getRand() Average %d\n", (int)(isum / ITERATIONS) );
fsum = 0.0;
for (i = 0 ; i < ITERATIONS ; i++) {
fsum += ((*getSRand)());
}
printf( "getSRand() Average %f\n", (fsum / (float)ITERATIONS) );
dlclose(lib);
return 0;
}
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
makefile
zad9: test.c
gcc -Wall -o zad9 test.c -ldl
librandapi.so: randapi.o initapi.o
gcc -shared -o librandapi.so randapi.o initapi.o
randapi.o: randapi.c randapi.h
gcc -c -Wall -fPIC randapi.c
initapi.o: initapi.c
gcc -c -Wall -fPIC initapi.c
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
zad9: test.c initapi.c
gcc -Wall -o zad9 test.c initapi.c -ldl
librandapi.so: randapi.o initapi.o
gcc -shared -o librandapi.so randapi.o
randapi.o: randapi.c randapi.h
gcc -c -Wall -fPIC randapi.c
Look at this line in man 3 dlopen:
If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) path‐name. Otherwise, the dynamic linker searches for the object as follows (see ld.so(8) for further details):
(and then a buch of rules that do not include the current directory nor the one where the executable is).
My guess is that you are copying librandapi.so to the current directory and that's why dlopen() cannot find it.
If that's the case, the solution is easy:
lib=dlopen("./librandapi.so", RTLD_LAZY);

Unable to link to ncurses in shared object

For some reason I have having an issue compiling a shared object that uses ncurses. Even though I include and link with -lncurses, compiling the .so file fails. Please advise.
#include <string.h>
#include "../include/mod_curse.h" /* Includes ncurses.h and friends */
int OnConsoleCmd(McapiS *API, ArgS *CmdArgs) /* Just ignore these, they're included in mod_curse.h */
{
if(!strcmp(CmdArgs->Data, "help"))
{
API->BPrintf(STD, "\n-- mod_curse.so --\n");
return 0;
}
}
int OnLoad(McapiS *API, va_list Args)
{
initscr(); /* These are the problems */
}
/* Time to clean up and unload the module */
int OnDeload(McapiS *API, va_list Args)
{
endwin();
}
Here is the Makefile:
CC = clang
CFLAGS = -Wall -fPIC
# Object Files
OBJ = mod_curse.o
# Header Files
INCLUDE = include/mod_curse.h
# Main Module
mod_setup.so: $(OBJ) $(INCLUDE)
$(CC) -shared -Wl,-soname,mod_curse.so,--no-undefined -o ../../mod_curse.so -lncurses $(OBJ)
# Source Files
mod_curse.o: src/mod_curse.c $(INCLUDE)
$(CC) $(CFLAGS) -c src/mod_curse.c
clean:
rm $(OBJ)
Here are the errors:
3 warnings generated.
clang -shared -Wl,-soname,mod_curse.so,--no-undefined -o ../../mod_curse.so -lncurses mod_curse.o
mod_curse.o: In function `OnLoad':
src/mod_curse.c:(.text+0x81): undefined reference to `initscr'
mod_curse.o: In function `OnDeload':
src/mod_curse.c:(.text+0xb1): undefined reference to `endwin'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mod_setup.so] Error 1
I needed to change my make command to have -lncurses appear after $(OBJ).

How can I link my C code with the PCRE library? (Linker errors currently being thrown.)

The Problem
Note: I originally had this problem in a much larger project; so I pared the code down to the test case you see below.
I cannot figure out how to get the following test code to compile. Specifically, it appears as if the linker cannot find the PCRE library (see below for how PCRE was configured). And this despite the explicit -L/usr/local/lib -lpcre being passed to the linker (PCRE is installed in the /usr/local directory structure).
What am I doing wrong? :-(
The console output is:
$ make
rm -f ./*.o
gcc -ansi -Wall -pedantic-errors -I/usr/local/include -g0 -O3 -static -static-libgcc -march=i686 -malign-double -m128bit-long-double -c main.c -o main.o
gcc -ansi -Wall -pedantic-errors -L/usr/local/lib -lpcre -g0 -O3 -static -static-libgcc -march=i686 -malign-double -m128bit-long-double main.o -o pcre_test_1_i686
main.o:main.c:(.text+0x100): undefined reference to `pcre_compile2'
main.o:main.c:(.text+0x12e): undefined reference to `pcre_study'
main.o:main.c:(.text+0x16e): undefined reference to `pcre_exec'
main.o:main.c:(.text+0x19f): undefined reference to `pcre_copy_substring'
collect2: ld returned 1 exit status
make: *** [all] Error 1
Relevant Files
PCRE Configuration and Compile Environment
./configure --disable-shared --enable-static --disable-cpp --enable-rebuild-chartables --enable-utf8 --enable-unicode-properties --enable-newline-is-any --disable-stack-for-recursion --with-posix-malloc-threshold=2 --with-link-size=4
CC="gcc"
CFLAGS="-g0 -O3 -static -static-libgcc -march=i686 -malign-double -m128bit-long-double"
LD_RUN_PATH="/usr/local/include:/usr/local/lib"
main.c
#define PCRE_STATIC
#include <pcre.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define OUTPUT_SIZE 12
#define SUBSTRING_SIZE 16
int main(int argc, char *argv[]) {
pcre *re;
pcre_extra *extra;
const char *input = "get food";
const char *pattern = "^\\s*get\\s+(\\w+)\\s*$\0";
int options = PCRE_CASELESS | PCRE_UTF8 | PCRE_UCP;
int error_code = 0;
int error_offset = 0;
const char *compile_error;
const char *study_error;
int output[OUTPUT_SIZE];
char substring[SUBSTRING_SIZE];
int matched = 0;
int is_error = 0;
int index = 0;
for(index = 0; index < OUTPUT_SIZE; index++) {
output[index] = 0;
}
re = pcre_compile2( pattern,
options,
&error_code,
&compile_error,
&error_offset,
NULL );
if(re == NULL) {
fprintf(stderr, "PCRE regular expression error at position %d: %s", error_offset, compile_error);
exit(EXIT_FAILURE);
}
if(error_code == 0) {
extra = pcre_study( re,
0,
&study_error );
}
else {
fprintf(stderr, "PCRE regular expression error at position %d: %s", error_offset, compile_error);
extra = NULL;
}
matched = pcre_exec( re,
extra,
input,
(int)strlen(input),
0, /* Start at the beginning of the string */
0,
output,
OUTPUT_SIZE );
if(matched > 1) {
int status = pcre_copy_substring( input,
output,
matched,
1,
substring,
SUBSTRING_SIZE );
if(status < 0) {
switch(status) {
case PCRE_ERROR_NOMEMORY:
fprintf(stderr, "PCRE substring extraction error: %s", "Buffer too small");
break;
case PCRE_ERROR_NOSUBSTRING:
fprintf(stderr, "PCRE substring extraction error: %s", "Invalid substring number");
break;
}
is_error = 1;
}
}
printf("Capture group 1 is: '%s'\n", substring);
if(is_error) {
printf("There was an error with the pcre_copy_substring() function.\n");
}
return EXIT_SUCCESS;
}
Makefile
PACKAGE = pcre_test
VERSION = 1
# -ansi == ANSI C
# -std=iso9899:199409 == ANSI C w/ Amendment 1
# -std=c99 == ISO C99
GCC_CMD = gcc -ansi -Wall -pedantic-errors
# Generic: i686
# Intel: core2, corei7, corei7-avx
# AMD: k8-sse3, opteron-sse3, athlon64-sse3, amdfam10
ARCH = i686
RELEASE_FILE = $(PACKAGE)_$(VERSION)_$(ARCH)
GCC_OPTIONS = -g0 -O3 -static -static-libgcc -march=$(ARCH) -malign-double -m128bit-long-double
GCC_COMPILE = $(GCC_CMD) -I/usr/local/include $(GCC_OPTIONS)
GCC_LINK = $(GCC_CMD) -L/usr/local/lib -lpcre $(GCC_OPTIONS)
GCC_LINK_ALL = $(GCC_LINK) main.o
all: clean build_main
$(GCC_LINK_ALL) -o $(RELEASE_FILE)
build_main:
$(GCC_COMPILE) -c main.c -o main.o
clean:
rm -f ./*.o
[EDIT]
The Answer Has Been Found!
All external library flags must be listed after all of the object files. Thus, the Makefile should have looked like this:
GCC_LINK = $(GCC_CMD) -L/usr/local/lib $(GCC_OPTIONS)
GCC_LINK_ALL = $(GCC_LINK) main.o -lpcre
Thank you to everyone who responded with answers. :-)
Is it possible the linker is finding a pre-built version of the libpcre.so?
Try the advice from this question:
"specify /usr/local/lib/libpcre.a on the compiler command line ... avoid including -lpcre".

Resources