This document provides an introduction and examples showing how to do profiling and test coverage analysis, and it also provides some of the technical details on how all this works. We're covering two sets of related functionalities here :

This document doesn't duplicate their documentation, so please consult the man pages of gprof and gcov for their description.

1. Getting started

The examples are also in the CeGCC source tree in directories docs/examples/coverage and docs/examples/profile.

1.1 Profiling

1.1.1 Summary

Profiling is triggered by using the compiler with the -pg command line option, a program compiled with it will generate a single file calles gmon.out. You can decode the gmon.out file by using the gprof tool.

1.1.2 Sample Makefile

This Makefile has pieces that can be copied in your own project's Makefile. It uses the pcp, prm, and prun commands from the Synce project.

.SUFFIXES:      .exe .c .h .rc .rsc .C

ARCH=           arm-wince-cegcc
CC=             ${ARCH}-gcc ${CFLAGS}

CFLAGS=         -g -D_WIN32_IE=0x0400 -pg

.o.exe:
        ${ARCH}-gcc ${CFLAGS} -o $@ $?

.c.exe:
        ${ARCH}-gcc ${CFLAGS} -o $@ $?

.C.exe:
        ${ARCH}-g++ ${CFLAGS} -o $@ $?

all::   fibo.exe fibo.explain

clean:
        -rm -f *.o *.exe *.rsc gmon.out *.explain *.gmon

dist:
        pcp fibo.exe ":/storage card/devel/fibo.exe"

fibo.explain:   fibo.gmon fibo.exe
	${ARCH}-gprof -q -x -l fibo.exe fibo.gmon > fibo.explain

fibo.gmon:       fibo.exe
        pcp fibo.exe ":/storage card/devel/fibo.exe"
        prun "/storage card/devel/fibo.exe"
        sleep 5
        pcp ":/storage card/devel/gmon.out" fibo.gmon
        prm "/storage card/devel/gmon.out"

1.1.3 Execution sample

When you run make, this happens :

dannypc: {10} make
arm-wince-cegcc-gcc -g -D_WIN32_IE=0x0400 -pg -o fibo.exe fibo.c
Info: resolving _CRT_MT by linking to __imp__CRT_MT (auto-import)
pcp fibo.exe ":/storage card/devel/fibo.exe"
File copy of 31491 bytes took 0 minutes and 1 seconds, that's 31491 bytes/s.
prun "/storage card/devel/fibo.exe"
sleep 5
pcp ":/storage card/devel/gmon.out" fibo.gmon
File copy took less than one second!
prm "/storage card/devel/gmon.out"
arm-wince-cegcc-gprof -q -x -l fibo.exe fibo.gmon > fibo.explain
dannypc: {11}

The top of the fibo.explain file looks like this :

                     Call graph (explanation follows)


granularity: each sample hit covers 4 byte(s) no time propagated

index % time    self  children    called     name
                0.00    0.00      19/21871       main (fibo.c:34 @ 11238) [45]
                0.00    0.00   21852/21871       fibo (fibo.c:12 @ 110e8) [28]
[1]      0.0    0.00    0.00   21871         fibo (fibo.c:9 @ 110b0) [1]

Run the commands for yourself and read the complete explanation in the output file.

1.2 Test coverage

1.2.1 Summary

Test coverage generates far more interesting results. It is triggered by several options to the compiler. An easy way to get started is to use --coverage.

An application built with this option will create a .gcda file for each source for compiled in the application. You can use the gcov tool to decode the contents of these files.

1.2.2 Sample Makefile

.SUFFIXES:      .exe .c .h .rc .rsc .C

ARCH=           arm-wince-cegcc
ARCH2=          arm-wince-mingw32ce

SE=             GCOV_CROSS_PREFIX="\\storage card\\devel"
CC=             ${SE} ${ARCH}-gcc ${CFLAGS}
WINDRES=        ${ARCH}-windres

EXECS=          fibo.exe m1.exe
CFLAGS=         -g -D_WIN32_IE=0x0400 --coverage

.rc.rsc:
                ${ARCH}-windres $? $@

.o.exe:
        ${SE} ${ARCH}-gcc ${CFLAGS} -o $@ $?

.c.exe:
        ${SE} ${ARCH}-gcc ${CFLAGS} -o $@ $?

.C.exe:
        ${SE} ${ARCH}-g++ ${CFLAGS} -o $@ $?

all::   ${EXECS}

clean:
                -rm -f *.o *.exe *.rsc *.gcno *.gcda *.gcov

dist:
        for i in $(EXECS) ; \
        do \
                pcp $$i ":/storage card/devel/"$$i ; \
        done

fibo.gcda:      fibo.exe
        pcp fibo.exe ":/storage card/devel/fibo.exe"
        -prm "/storage card/devel/fibo.gcda"
        prun "/storage card/devel/fibo.exe"
        sleep 5
        pcp ":/storage card/devel/fibo.gcda" fibo.gcda
        -prm "/storage card/devel/fibo.gcda"

fibo.c.gcov: fibo.gcda
        arm-wince-cegcc-gcov -a fibo.c

all::   fibo.c.gcov

#
# The m1.exe example uses the mingw32ce target,
# and demonstrates multiple source files.
#
m1.exe: m1.o a.o b.o
        ${SE} ${ARCH2}-gcc ${CFLAGS} -o m1.exe a.o b.o m1.o

m1.o:   m1.c
        ${SE} ${ARCH2}-gcc ${CFLAGS} -c m1.c

a.o:    a.c
        ${SE} ${ARCH2}-gcc ${CFLAGS} -c a.c

b.o:    b.c
        ${SE} ${ARCH2}-gcc ${CFLAGS} -c b.c

a.gcda b.gcda m1.gcda:  m1.exe
        pcp m1.exe ":/storage card/devel/m1.exe"
        -prm "/storage card/devel/m1.gcda"
        -prm "/storage card/devel/a.gcda"
        -prm "/storage card/devel/b.gcda"
        prun "/storage card/devel/m1.exe"
        sleep 5
        pcp ":/storage card/devel/m1.gcda" m1.gcda
        pcp ":/storage card/devel/a.gcda" a.gcda
        pcp ":/storage card/devel/b.gcda" b.gcda
        -prm "/storage card/devel/m1.gcda"
        -prm "/storage card/devel/a.gcda"
        -prm "/storage card/devel/b.gcda"

m1.c.gcov: m1.gcda
        arm-wince-cegcc-gcov -a m1.c

a.c.gcov: a.gcda
        arm-wince-cegcc-gcov -a a.c

b.c.gcov: b.gcda
        arm-wince-cegcc-gcov -a b.c

all::   m1.c.gcov a.c.gcov b.c.gcov

1.2.3 Execution sample

dannypc: {33} make
GCOV_CROSS_PREFIX="\\storage card\\devel" arm-wince-cegcc-gcc -g -D_WIN32_IE=0x0400 --coverage -o fibo.exe fibo.c
Info: resolving _CRT_MT by linking to __imp__CRT_MT (auto-import)
GCOV_CROSS_PREFIX="\\storage card\\devel" arm-wince-mingw32ce-gcc -g -D_WIN32_IE=0x0400 --coverage -c m1.c
GCOV_CROSS_PREFIX="\\storage card\\devel" arm-wince-mingw32ce-gcc -g -D_WIN32_IE=0x0400 --coverage -c a.c
GCOV_CROSS_PREFIX="\\storage card\\devel" arm-wince-mingw32ce-gcc -g -D_WIN32_IE=0x0400 --coverage -c b.c
GCOV_CROSS_PREFIX="\\storage card\\devel" arm-wince-mingw32ce-gcc -g -D_WIN32_IE=0x0400 --coverage -o m1.exe a.o b.o m1.o
pcp fibo.exe ":/storage card/devel/fibo.exe"
File copy took less than one second!
prm "/storage card/devel/fibo.gcda"
prm: Failed to remove '\storage card\devel\fibo.gcda': File not found
gmake: [fibo.gcda] Error 1 (ignored)
prun "/storage card/devel/fibo.exe"
sleep 5
pcp ":/storage card/devel/fibo.gcda" fibo.gcda
File copy took less than one second!
prm "/storage card/devel/fibo.gcda"
arm-wince-cegcc-gcov -a fibo.c
File 'fibo.c'
Lines executed:100.00% of 20
fibo.c:creating 'fibo.c.gcov'

pcp m1.exe ":/storage card/devel/m1.exe"
File copy of 39012 bytes took 0 minutes and 1 seconds, that's 39012 bytes/s.
prm "/storage card/devel/m1.gcda"
prm: Failed to remove '\storage card\devel\m1.gcda': File not found
gmake: [m1.gcda] Error 1 (ignored)
prm "/storage card/devel/a.gcda"
prm: Failed to remove '\storage card\devel\a.gcda': File not found
gmake: [m1.gcda] Error 1 (ignored)
prm "/storage card/devel/b.gcda"
prm: Failed to remove '\storage card\devel\b.gcda': File not found
gmake: [m1.gcda] Error 1 (ignored)
prun "/storage card/devel/m1.exe"
sleep 5
pcp ":/storage card/devel/m1.gcda" m1.gcda
File copy took less than one second!
pcp ":/storage card/devel/a.gcda" a.gcda
File copy took less than one second!
pcp ":/storage card/devel/b.gcda" b.gcda
File copy took less than one second!
prm "/storage card/devel/m1.gcda"
prm "/storage card/devel/a.gcda"
prm "/storage card/devel/b.gcda"
arm-wince-cegcc-gcov -a m1.c
File '/opt/mingw32ce/lib/gcc/arm-wince-mingw32ce/4.1.0/../../../../arm-wince-mingw32ce/include/string.h'
Lines executed:0.00% of 3
/opt/mingw32ce/lib/gcc/arm-wince-mingw32ce/4.1.0/../../../../arm-wince-mingw32ce/include/string.h:creating 'string.h.gcov'

File '/opt/mingw32ce/lib/gcc/arm-wince-mingw32ce/4.1.0/../../../../arm-wince-mingw32ce/include/stdlib.h'
Lines executed:0.00% of 5
/opt/mingw32ce/lib/gcc/arm-wince-mingw32ce/4.1.0/../../../../arm-wince-mingw32ce/include/stdlib.h:creating 'stdlib.h.gcov'

File 'm1.c'
Lines executed:100.00% of 10
m1.c:creating 'm1.c.gcov'

arm-wince-cegcc-gcov -a a.c
File 'a.c'
Lines executed:100.00% of 3
a.c:creating 'a.c.gcov'

arm-wince-cegcc-gcov -a b.c
File 'b.c'
Lines executed:100.00% of 6
b.c:creating 'b.c.gcov'

dannypc: {34} 

The output above includes some interesting results already : it mentions the amount of source code executed per source file. The contents of the .gcov files are even more interesting. This is the contents of b.c.gcov :

        -:    0:Source:b.c
        -:    0:Graph:b.gcno
        -:    0:Data:b.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:/*
        -:    2: * Profiling test
        -:    3: */
        -:    4:int fibo(int x)
  2692507:    5:{
  2692507:    6:        if (x < 3)
  2692507:    6-block  0
  1346268:    7:                return 1;
  1346268:    7-block  0
  2692507:    8:        return fibo(x-1) + fibo(x-2);
  1346239:    8-block  0
  2692507:    8-block  1
        -:    9:}
        -:   10:
        -:   11:int func_b(int i)
       29:   12:{
       29:   13:        return fibo(i);
       29:   13-block  0
        -:   14:}

The seven digit numbers in the first column are the number of times these source lines were executed. Modify m1.c so fibo() is called fewer times, and you'll see more modest numbers.

2 Technical overview : CeGCC (gcc) profiling and coverage support

This piece of documentation is not strictly related to profiling and test coverage support, the normal operation is also described.

2.1 Overview

The tables below present an overview of how this all works, the details are described further in this document.

The behaviour of the arm-wince-cegcc-gcc compiler :
cegcc target Compiler option Startup files Startup calls Any source calls Exit calls (by atexit)
Normal crt0.o crtst.o N/A N/A N/A
Profiling -pg crt0.o crtst.o gcrt3.o monstartup mcount mcleanup
Coverage --coverage crt0.o crtst.o N/A _gcov_init gcov_exit

The behaviour of the arm-wince-mingw32ce-gcc compiler :
mingw32ce target Compiler option Startup files Startup calls Any source calls Exit calls (by atexit)
Normal crt3.o N/A N/A N/A
Profiling -pg crt3.o gcrt3.o monstartup mcount mcleanup
Coverage --coverage crt3.o N/A _gcov_init gcov_exit

2.2 Profiling

Profiling support depends on special startup code (in gcrt3.o) and an exit handler (mcleanup) installed by it.

2.2.1 gcrt3.o

The source of gcrt3.o is in src/mingw/profile/gcrt0.c. It provides for the function _monstartup whose purpose is to call monstartup and register _mcleanup as an exit handler via atexit.

Both _mcleanup and monstartup are in libgmon.a, their source is src/mingw/profile/gmon.c.

2.3 Coverage

For gcov support, there is no special startup function, rather the compiler generates a call to _gcov_init from all compiled source files. The support functions are in src/gcc/gcc/libgcov.c and src/gcc/gcc/gcov-io.c.

2.4 Ways in which WinCE applications start and terminate

2.4.1 Startup

2.4.1.1 main

main is the entry point for C language programs in college textbooks, it's also the default entry point generated by cegcc. crt0.o will call main.

2.4.1.2 WinMain

The normal startup for an application with a GUI (graphical user interface) on a Windows platform is WinMain. CeGCC implements that through the main.o module in libc.a or libcegcc.dll.

The main.o module defines a main function which calls WinMain. To figure out the parameters for that function, it calls a number of WinCE functions such as GetCommandLineW. When you compile a program that has a main function of its own (see the previous paragraph), the main.o is simply not used. The source for main.o is in src/newlib/newlib/libc/sys/wince/main.c.

2.4.2 Termination

This chapter describes the various ways in which a Windows CE application can terminate.

2.4.2.1 WinMain WM_DESTROY

The WM_DESTROY message is sent to an application by the system e.g. when the user hits the button to close that window. The typical response of an application is to call PostQuitMessage.

2.4.2.2 WinMain PostQuitMessage

The PostQuitMessage function posts a request to terminate this thread or application. It does so by sending a WM_QUIT message to the thread or application.

In response to WM_QUIT, the normal message loop will terminate.

	BOOL r;

	while ((r = GetMessageW(&msg, wnd, 0, 0)) != 0) {
		if (r != -1) {
			TranslateMessage(&msg);
			DispatchMessageW(&msg);
		} else {
			// Handle the error
		}
	}

This is implemented in the GetMessageW function : when the function receives the WM_QUIT message, its return value is 0.

2.4.2.3 exit

2.4.2.3.1 arm-wince-cegcc exit

In our arm-wince-cegcc target, this is implemented in the system layer of newlib. Startup and termination are both coded in src/newlib/newlib/libc/sys/wince/startup.c.

The exit function (part of the stdlib part of newlib, in src/newlib/newlib/libc/stdlib/) does little more than calling _exit. The latter function jumps back into the _startup_ function by using longjmp and performs newlib cleanup, and finally calls ExitProcess which is a Windows CE System Call.

2.4.2.3.2 arm-wince-mingw32ce exit

In our arm-wince-mingw32ce target, exit is implemented in src/mingw/atexit.c. It calls a cleanup function and ExitProcess.

2.4.2.4 WinMain / main fall through

On arm-wince-cegcc, falling through the end of main gets us to the same place where calling exit leads to. On arm-wince-mingw32ce, this is not true, but the startup function (WinMainCRTStartup, e.g. in src/mingw/crt3.c) calls the same functions as exit.

Information

Support

Powered by Sourceforge.net