<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christophe Blaess</title>
	<atom:link href="http://www.blaess.fr/christophe/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.blaess.fr/christophe</link>
	<description>Ingénierie et formations sur Linux et les logiciels libres</description>
	<lastBuildDate>Mon, 20 Feb 2012 10:18:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Options de compilation pour Linux industriel (1)</title>
		<link>http://www.blaess.fr/christophe/2012/02/19/options-de-compilation-pour-linux-industriel/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/19/options-de-compilation-pour-linux-industriel/#comments</comments>
		<pubDate>Sun, 19 Feb 2012 19:00:26 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Embarqué]]></category>
		<category><![CDATA[Formations]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1666</guid>
		<description><![CDATA[J&#8217;ai remarqué une question récurrente, tant durant mes sessions de formation sur Linux industriel qu&#8217;au cours de prestations d&#8217;ingénierie concernant des systèmes temps-réel ou embarqués : &#171;&#160;Quelles sont les options du noyau qui influent sur [un sujet donné] ?&#160;&#187; Le sujet en question a généralement trait aux mécanismes d&#8217;ordonnancement, à la vitesse de boot, à [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/02/Capture.png"><img class="alignright size-thumbnail wp-image-1696" title="make menuconfig" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/02/Capture-150x150.png" alt="make menuconfig" width="150" height="150" /></a></p>
<p style="text-align: justify;">J&#8217;ai remarqué une question récurrente, tant durant mes <a title="Formations" href="http://www.blaess.fr/christophe/formations/" target="_blank">sessions de formation</a> sur Linux industriel qu&#8217;au cours de <a title="Prestations ingénierie Linux" href="http://www.logilin.fr/Prestations-ingenierie-Linux.html" target="_blank">prestations d&#8217;ingénierie</a> concernant des systèmes temps-réel ou embarqués : &laquo;&nbsp;Quelles sont les options du noyau qui influent sur <em>[un sujet donné]</em> ?&nbsp;&raquo; Le sujet en question a généralement trait aux mécanismes d&#8217;ordonnancement, à la vitesse de <em>boot</em>, à la taille du code produit, etc.</p>
<p style="text-align: justify;">Donner une réponse exhaustive est impossible mais au fil du temps, j&#8217;ai constitué une petite liste des options les plus importantes à vérifier lors de la compilation du noyau, pour optimiser certains aspects. Ces paramètres sont parfois complémentaires parfois antagonistes. Par exemple le souci d&#8217;économie énergétique sur un système embarqué autonome est aux antipodes des problématiques de temps de réponse aux interruptions en temps-réel.</p>
<p style="text-align: justify; padding-left: 60px;">Bien entendu, je ne traite pas ici des options permettant au kernel de fonctionner (reconnaître les périphériques présents, disposer des protocoles réseau, gérer les types de systèmes de fichiers nécessaires&#8230;) Je considère qu&#8217;elles sont indispensables pour que le noyau démarre. Ce qui m&#8217;intéresse ici, ce sont les options de compilation susceptibles d&#8217;améliorer le comportement dans un environnement industriel.</p>
<p style="text-align: justify;">Je prends comme version de référence le noyau <a title="Linux 3.2" href="http://www.kernel.org/pub/linux/kernel/v3.x/linux-3.2.tar.bz2" target="_blank"><code>linux-3.2.tar.bz2</code></a> et je vais passer en revue les principaux menus de configuration que l&#8217;on peut observer avec  &laquo;&nbsp;<code>make menuconfig</code>&nbsp;&raquo; &laquo;&nbsp;<code>make xconfig</code>&nbsp;&raquo; &laquo;&nbsp;<code>make gconfig</code>&nbsp;&raquo; etc. Les paramètres décrits ci-dessous proviennent d&#8217;une configuration pour processeur <em>x86</em>, mais je décrirai également quelques options spécifiques à d&#8217;autres architectures.</p>
<h1> Menu <em>General Setup</em></h1>
<ul>
<li style="text-align: justify;"><strong><em>Cross-compiler tool prefix</em></strong> : lors d&#8217;une compilation croisée (pour un autre processeur), on indiquera ici le préfixe nécessaire pour trouver la <em>cross-toolchain</em> (que l&#8217;on peut <a title="Buildroot vs Crosstool-NG – Compilons notre cross-compiler" href="http://www.blaess.fr/christophe/2011/05/27/buildroot-vs-crosstool-ng-compilons-notre-cross-compiler/">générer de différentes manières</a>). Par exemple <code>/opt/cross-arm/usr/bin/arm-linux-</code> indiquera que les appels au compilateur <code>gcc</code> devront se transformer en <code>/opt/cross-arm/usr/bin/arm-linux-gcc</code>, et ainsi de suite pour les autres outils de compilation (<code>as</code>, <code>ld</code>, <code>ar</code>, etc.). Il s&#8217;agit d&#8217;une option que l&#8217;on peut également surcharger en remplissant la variable d&#8217;environnement <code>CROSS_COMPILE</code> avant d&#8217;appeler <code>make</code>.</li>
<li style="text-align: justify;"><strong><em>Kernel compression mode</em></strong> : pour optimiser la vitesse de boot on préférera une compression <code>LZO</code>, pour réduire au maximum la taille du noyau en mémoire flash (avant transfert dans la Ram) on choisira <code>LZMA</code>, pour avoir un équilibre entre ces deux paramètres j&#8217;utiliserais <code>XZ</code>. Sur certaines architectures, toutes les compressions ne sont pas disponibles.</li>
<li style="text-align: justify;"><strong><em>Support for paging of anonymous memory (swap)</em></strong> : sur la plupart des systèmes industriels, l&#8217;utilisation de <em>swap</em> (partition ou fichier sur disque dans lesquels on transfère temporairement une partie de la mémoire Ram en cas de saturation) est proscrite. En effet le système de fichiers est généralement en mémoire flash qui ne supporte pas les écritures intensives. En outre cela diminue la prédictibilité des temps d&#8217;accès. Je désactive habituellement cette option afin de ne pas risque d&#8217;activer malencontreusement le <em>swap</em> ultérieurement.</li>
<li style="text-align: justify;"><strong><em>System V IPC</em></strong> et <strong><em>Posix Message Queues</em></strong> : ces mécanismes de communication entre processus (<a title="Efficacité des IPC : les files de messages Posix" href="http://www.blaess.fr/christophe/2011/09/17/efficacite-des-ipc-les-files-de-messages-posix/">files de messages</a>, sémaphores, <a title="Efficacite des IPC : sémaphore et mémoire partagée" href="http://www.blaess.fr/christophe/2011/10/09/efficacite-des-ipc-semaphore-et-memoire-partagee/">mémoire partagée</a>) sont souvent employés dans les systèmes industriels, sauf si le code applicatif est constitué seulement de threads regroupé dans un unique processus. Généralement j&#8217;active ces deux options.</li>
<li style="text-align: justify;"><strong><em>BSD Process accounting</em></strong>, <strong><em>Export task/process statistics</em></strong>, et <strong><em>Auditing Support</em></strong> : si ces options d&#8217;instrumentation peuvent être utiles pendant la phase de mise au point du code, elles n&#8217;ont plus lieu d&#8217;être activées sur un système en production. Je conseille de les désactiver (ce qui peut nécessiter d&#8217;intervenir dans les menus <strong><em>Security options</em></strong> et <strong><em>Virtualization</em></strong> que nous verrons dans d&#8217;autres articles).</li>
<li style="text-align: justify;">Sous-menu <strong><em>RCU Subsystem</em></strong> : L&#8217;option <strong><em>Enable RCU priority boosting</em></strong> permet de limiter les inversions de priorité liées aux mécanismes de synchronisation <em>Read-Copy-Update</em>. Ceci peut être utile &#8211; bien que d&#8217;un effet limité &#8211; dans un système temps-réel à condition de mettre une valeur élevée (<code>99</code> par exemple) pour <strong><em>Real-time priority to boost RCU readers to</em></strong>.</li>
<li style="text-align: justify;"><strong><em>Kernel .config support</em></strong> et <strong><em>Enable access to .config through /proc/config.gz</em></strong> : ces deux options sont à mon avis très utiles. Elles permettent d&#8217;embarquer dans l&#8217;image du noyau compilé sa propre configuration (réalisée avec <code>make menuconfig</code>). La mise au point d&#8217;un système embarqué ou temps-réel peut nécessiter des dizaines de compilations et tests successifs en modifiant peu à peu les options décrites ici. Il n&#8217;est pas simple de garder une trace exacte de toutes les modifications apportées et la discipline nécessaire pour sauvegarder et documenter chaque fois le fichier <code>.config</code> vient souvent à manquer après des heures de frustrations et d&#8217;échecs successifs. Le fait que chaque noyau compilé puisse restituer sa propre configuration nous évite de gérer les versions et permet facilement de comparer les options utilisées.</li>
<li style="text-align: justify;">Sous-menu <strong><em>Control Group support</em></strong> : la plupart des options de ce sous-menu peuvent être utiles pour gérer finement l&#8217;ordonnancement des tâches et la répartition des ressources (CPU, mémoire, entrées-sorties du sous-système <em>Block</em>, etc.). Ces paramètres peuvent servir à gérer des tâches en temps-partagé mais également en temps-réel (notamment <strong><em>Group CPU scheduler</em></strong> &#8211;&gt; <strong><em>Group scheduling for SCHED_RR/FIFO</em></strong>).</li>
<li style="text-align: justify;">J&#8217;ai évoqué l&#8217;option <strong><em>Automatic process group scheduling</em></strong> dans <a title="[ACTU] Groupement automatique des processus" href="http://www.blaess.fr/christophe/2011/04/01/groupement-automatique-des-processus/">un article précédent</a>. Elle active le regroupement automatique des processus rattachés au même terminal pour leur donner une part de CPU équivalente aux autres groupes en ordonnancement temps-partagé.</li>
<li style="text-align: justify;">Sur de nombreux systèmes embarqués l&#8217;option <strong><em>Initial RAM filesystem and RAM disk</em></strong> sera nécessaire car elle permettra de démarrer avec un système de fichiers monté en mémoire Ram (quitte à accéder ensuite à des partitions différentes sur mémoire flash). On choisira en principe le même type de compression (<code>LZMA</code>, <code>XZ</code>, <code>LZO</code>&#8230;) que celui de l&#8217;option <em>Kernel compression mode</em> vue plus haut, en préférant un volume réduit ou une décompression plus rapide suivant les cas.</li>
<li style="text-align: justify;">L&#8217;option <strong><em>Optimize for size</em></strong> sera activée sur les systèmes embarqués pour demander au compilateur de réduire la taille de l&#8217;image du noyau ; de même on désactivera <strong><em>Enable full-sized data structures for core</em></strong> qui se trouve un peu plus bas</li>
<li style="text-align: justify;">Le sous-menu <strong><em>Configure standard kernel features (expert users)</em></strong> n&#8217;est parfois accessible que si l&#8217;option <strong><em>Embedded System</em></strong> (plus bas) est activée &#8211; ce qu&#8217;il faut faire. On peut désactiver toutes les options du sous-menu sur la plupart des systèmes embarqués. Le noyau deviendra totalement silencieux (pas de messages de trace avec <code>printk()</code>, pas d&#8217;avertissement avec <code>WARN()</code>, pas d&#8217;indication d&#8217;erreur interne avec <code>BUG()</code>) et ne nous fournira plus d&#8217;éléments de diagnostic. En revanche sa taille sera réduite et le <em>boot</em> sensiblement plus rapide.</li>
<li style="text-align: justify;">J&#8217;active systématiquement <strong><em>Enable futex support</em></strong> pour bénéficier des mutex rapides (verrouillage sans passage par le noyau s&#8217;il n&#8217;y a pas de contention, pour plus de détails voir cet <a title="Prises de mutex et priorités" href="http://www.blaess.fr/christophe/2011/10/22/prises-de-mutex-et-priorites/">article précédent</a>).</li>
<li style="text-align: justify;">Toutes les options <strong><em>Profiling support</em></strong>, <strong><em>Kprobes</em></strong>, <strong><em>Optimize trace point call sites</em></strong>, et<strong><em> GCOV-based kernel profiling</em></strong> devront être désactivées sur le noyau final. Elles peuvent être trés utiles durant la mise au point du système, mais ne feraient que charger inutilement l&#8217;image cible.</li>
</ul>
<p style="text-align: justify;">Les options qui n&#8217;ont pas été mentionnées seront activées ou non en fonction de l&#8217;utilisation de votre système. Si l&#8217;appel-système <code>signalfd()</code> par exemple n&#8217;est jamais employé, l&#8217;option <strong><em>Enable signalfd() system call</em></strong> n&#8217;a pas vraiment d&#8217;intérêt. Néanmoins on ne gagnera pas grand-chose à la désactiver.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">Voici un premier tableau récapitulatif des options à activer (Y) ou désactiver (N) en fonction des optimisations recherchées. Les options non mentionnées ici n&#8217;ont pas d&#8217;influence directe sur les aspects embarqués (taille mémoire et vitesse de boot) ou les performances temps-réel.</p>
<table border="1" cellspacing="2" cellpadding="3">
<tbody>
<tr align="center">
<td><strong>Option</strong></td>
<td style="text-align: center;"><strong>Optimisation<br />
pour<br />
embarqué</strong></td>
<td style="text-align: center;"><strong>Optimisation<br />
pour<br />
temps-réel</strong></td>
</tr>
<tr align="center">
<td>Kernel compression mode</td>
<td>LZMA / LZO</td>
<td></td>
</tr>
<tr align="center">
<td>Support for paging of anonymous memory</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>BSD Process accounting</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Open by fhandle syscalls</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Export statistics through netlink</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Auditing support</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>RCU Subsystem -&gt; Enable tracing for RCU</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>RCU Subsystem -&gt; Enable RCU priority boosting</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Kernel .config support</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Enable access to .config through /proc/config.gz</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Example debug cgroup&#8230;</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Freezer cgroup subsystem</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Device controller&#8230;</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Cpuset support</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Group CPU -&gt; &#8230;SCHED_RR/FIFO</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Block IO controller</td>
<td>Y</td>
<td>Y</td>
</tr>
<tr align="center">
<td>Control Group Support -&gt; Enable Block IO controller debugging</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Namespace support</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Automatic process group scheduling</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Initial RAM filesystem and RAM disk</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Support initial RAM disk cmopressed using LZMA</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Support initial RAM disk cmopressed using XZ</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Support initial RAM disk cmopressed using LZO</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Optimize for size</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Configure standard kernel features -&gt; Enable 16-bit UID&#8230;</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Configure standard kernel features -&gt; Sysctl syscall support</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Configure standard kernel features -&gt; Enable support for printk</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Configure standard kernel features -&gt; BUG() support</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Configure standard kernel features -&gt; Enable Elf core dumps</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Enable full-sized data structures for core</td>
<td>N</td>
<td></td>
</tr>
<tr align="center">
<td>Enable futex support</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Enable AIO support</td>
<td></td>
<td>Y</td>
</tr>
<tr align="center">
<td>Embedded system</td>
<td>Y</td>
<td></td>
</tr>
<tr align="center">
<td>Kernel performance&#8230; -&gt; Kernel performance counters</td>
<td></td>
<td>N</td>
</tr>
<tr align="center">
<td>Kernel performance&#8230; -&gt; Debug; use vmalloc to&#8230;</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Enable VM event counters for /proc/vmstat</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Profiling support</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Kprobes</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>Optimize trace point call sites</td>
<td>N</td>
<td>N</td>
</tr>
<tr align="center">
<td>GCOV-based kernel profiling -&gt; Enable gcov-based &#8230;</td>
<td>N</td>
<td>N</td>
</tr>
</tbody>
</table>
<p style="text-align: justify;">Nous examinerons les autres menus dans les prochains articles&#8230;</p>
<p style="text-align: center;">Tous les commentaires, rermarques, corrections, sont les bienvenus&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/19/options-de-compilation-pour-linux-industriel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mise au point de bibliothèque dynamique (3/3)</title>
		<link>http://www.blaess.fr/christophe/2012/02/12/mise-au-point-de-bibliotheque-dynamique-33/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/12/mise-au-point-de-bibliotheque-dynamique-33/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 09:00:46 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1612</guid>
		<description><![CDATA[(english translation here) Nous avons commencé la mise au point d&#8217;une bibliothèque dynamique sous Linux dans les deux articles précédents : dans le premier nous avons vu comment compiler la bibliothèque et gérer les numéros de versions à l&#8217;aide de liens symboliques, dans le second nous avons effectué du suivi d&#8217;appel et du débogage pas-à-pas. [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(english translation <a title="Development of a dynamic library (3/3)" href="http://www.blaess.fr/christophe/2012/02/12/development-of-a-dynamic-library-33/">here</a>)</p>
<p style="text-align: justify;">Nous avons commencé la mise au point d&#8217;une bibliothèque dynamique sous Linux dans les deux articles précédents : dans <a title="Mise au point de bibliothèque dynamique (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/" target="_blank">le premier</a> nous avons vu comment compiler la bibliothèque et gérer les numéros de versions à l&#8217;aide de liens symboliques, dans <a title="Mise au point de bibliothèque dynamique (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/" target="_blank">le second</a> nous avons effectué du suivi d&#8217;appel et du débogage pas-à-pas. Nous allons désormais nous intéresser à la vérification de la couverture de la bibliothèque.</p>
<p style="padding-left: 60px; text-align: justify;">La <em><strong>couverture de code</strong></em> est une mesure indiquant le pourcentage de lignes de code qui ont été effectivement parcourues pendant une exécution d&#8217;un programme. On élargit peu à peu le jeu de tests afin d&#8217;obtenir une couverture de 100% (et s&#8217;assurer que le code a été intégralement vérifié).</p>
<p style="text-align: justify;"> L&#8217;outil par excellence sous Linux est <code>gcov</code>, qui instrumente le code source et nous fournit des statistiques détaillées après exécution. Il est facile de l&#8217;utiliser pour vérifier la couverture d&#8217;un fichier source compilé directement dans un exécutable ; nous allons l&#8217;employer pour le code d&#8217;une bibliothèque ce qui nécessite quelques suppléments d&#8217;attention.</p>
<h1> Compilation</h1>
<p style="text-align: justify;">Reprenons les mêmes fichiers et répertoires (regroupés <a title="http://www.blaess.fr/christophe/files/article-2012-02-12/factorielle-3.tar.bz2" href="http://www.blaess.fr/christophe/files/article-2012-02-12/factorielle-3.tar.bz2" target="_blank">dans cette archive</a>) que pour les articles précédents. Un répertoire  principal nommé &laquo;&nbsp;<code>factorielle</code>&nbsp;&raquo; contient quatre sous-répertoires : <code>src</code>, <code>include</code> et <code>lib</code> où se trouvent respectivement les fichiers sources, les fichiers d&#8217;en-tête et les fichiers compilés de la bibliothèque. Le quatrième sous-répertoire &laquo;&nbsp;<code>test</code>&nbsp;&raquo; contient les fichiers source et exécutable d&#8217;un programme utilisant notre bibliothèque <code>libfact</code>.</p>
<pre>[~]$ <strong>cd factorielle/</strong>
[factorielle]$ <strong>ls</strong>
include  lib  src  test
[factorielle]$ <strong>ls include/</strong>
fact.h
[factorielle]$ <strong>ls lib/</strong>
libfact.so  libfact.so.2  libfact.so.2.0
[factorielle]$ <strong>ls src/</strong>
fact.c
[factorielle]$ <strong>ls test/</strong>
calcule-factorielle.c
[factorielle]$</pre>
<p style="padding-left: 60px; text-align: justify;">NB: la bibliothèque était déjà compilée ci-dessus, mais nous allons la régénérer.</p>
<p style="text-align: justify;">La première étape consiste à compiler le code de la bibliothèque. Nous allons procéder comme dans le <a title="Mise au point de bibliothèque dynamique (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/" target="_blank">premier article</a>, mais cette fois en ajoutant l&#8217;option <code>--coverage</code> de <code>gcc</code>. Cette option a deux rôles différents .</p>
<ul>
<li style="text-align: justify;">Lors de la phase de compilation elle a la même signification que <code>-fprofile-arcs</code> et <code>-ftest-coverage</code> (que l&#8217;on utilisaient avec les versions précédentes de <code>gcc</code>) : la première ajoute dans le code exécutable des informations d&#8217;instrumentation (compteurs de passage), la seconde crée une table de correspondance entre ces éléments et les lignes de code source (un fichier nommé à partir du fichier source avec l&#8217;extension <code>.gcno</code>)</li>
<li style="text-align: justify;">Pendant la phase d&#8217;édition des liens, elle est équivalente à <code>-lgcov</code> (qui était ajoutée automatiquement par <code>-ftest-coverage</code>) qui incorpore les points d&#8217;entrée nécessaire pour l&#8217;emploi ultérieur de <code>gcov</code>.</li>
</ul>
<p style="text-align: justify;">Voici la compilation de notre bibliothèque.</p>
<pre>[factorielle]$ <strong>gcc -c --coverage -fPIC -I include/ -o ./src/fact.o ./src/fact.c </strong>
[factorielle]$ <strong>ls src/</strong>
fact.c  fact.gcno  fact.o
[factorielle]$</pre>
<p style="text-align: justify;">Nous voyons qu&#8217;avec l&#8217;option <code>--coverage</code> la compilation a généré, outre le fichier objet <code>fact.o</code>, un fichier <code>fact.gcno</code> contenant les relations entre les blocs de code et les numéros de lignes. Continuons.</p>
<pre>[factorielle]$ <strong>gcc -shared -I include/ -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 ./src/fact.o --coverage </strong>
[factorielle]$ <strong><code>ls -l lib/</code></strong>
total 20
lrwxrwxrwx 1 cpb cpb    12 2012-02-11 13:39 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb    14 2012-02-11 13:39 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 16866 2012-02-11 17:10 libfact.so.2.0
[factorielle]$</pre>
<p style="text-align: justify;">Nous avons re-créé la bibliothèque <code>libfact.so.2.0</code>. Les liens symboliques permettent de gérer les numéros de version majeurs et mineurs, comme nous l&#8217;avons vu dans le premier article. Compilons à présent un fichier exécutable, sans l&#8217;option <code>--coverage</code> (ou conservons le fichier exécutable des articles précédents).</p>
<pre>[factorielle]$ <strong>gcc -I include/ -L lib/ -o test/calcule-factorielle test/calcule-factorielle.c -lfact</strong>
[factorielle]$ <strong>ls test/</strong>
calcule-factorielle  calcule-factorielle.c
[factorielle]$</pre>
<h1>Exécution</h1>
<p style="text-align: justify;">L&#8217;exécution du programme se déroule tout à fait normalement (bien qu&#8217;en pratique il soit légèrement ralenti). Il faut, bien sûr, penser à renseigner la variable d&#8217;environnement <code>LD_LIBRARY_PATH</code> pour indiquer où l&#8217;éditeur de lien dynamique trouvera la bibliothèque nécessaire au fonctionnement de l&#8217;application.</p>
<pre>[factorielle]$ <strong>export LD_LIBRARY_PATH=lib/</strong>
[factorielle]$ <strong>test/calcule-factorielle 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorielle]$ <strong>ls src/</strong>
fact.c  fact.gcda  fact.gcno  fact.o
[factorielle]$</pre>
<p style="text-align: justify;">Un nouveau fichier <code>fact.gcda</code> est apparu, qui contient les statistiques d&#8217;exécution des blocs de <code>fact.gcno</code> (et des transitions entre blocs).</p>
<h1>Exploitation des résultats</h1>
<p style="text-align: justify;">Pour obtenir des informations sur la couverture du code d&#8217;un fichier source, nous invoquons <code>gcov</code> en indiquant le nom du fichier source. Les données sont en effet valable indépendamment pour chaque fichier source de l&#8217;application (ou de la bibliothèque).</p>
<p style="text-align: justify;">Nous allons utiliser l&#8217;option <code>-o</code> de <code>gcov</code> pour préciser le nom du répertoire où se trouvent les fichiers <code>.c</code>, <code>.gcno</code> et <code>.gcda</code>.</p>
<pre>[factorielle]$ <strong>gcov -o src/ fact.c</strong>
File './src/fact.c'
Lignes exécutées: 87.50% de 8
./src/fact.c:creating 'fact.c.gcov'

[factorielle]$</pre>
<p style="text-align: justify;"><code>gcov</code> nous indique que nous n&#8217;avons exécuté que 87.5% des huit lignes de code de notre fonction. Comment savoir ce qui s&#8217;est passé ? Nous voyons que <code>gcov</code> a aussi créé un fichier <code>fact.c.gcov</code> dans lequel il reprend notre code source, numérote les lignes, ajoute un en-tête et une colonne de statistiques en début de ligne.</p>
<pre>[factorielle]$ <strong>ls</strong>
fact.c.gcov  include  lib  src  test
[factorielle]$ <strong>cat fact.c.gcov </strong>
        -:    0:Source:./src/fact.c
        -:    0:Graph:src/fact.gcno
        -:    0:Data:src/fact.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include
        -:    2:
        3:    3:int factorielle(long int n, long long int * result)
        -:    4:{
        3:    5:	* result = 1;
        3:    6:	if (n &lt; 0)
    #####:    7:		return -1;
        -:    8:	do {
       12:    9:		(*result) = (*result) * n;
       12:   10:		n = n - 1;
       12:   11:	} while (n &gt; 1);
        3:   12:	return 0;
        -:   13:}
        -:   14:
[factorielle]$</pre>
<p style="text-align: justify;">L&#8217;en-tête décrit les fichiers concernés et le nombre d&#8217;exécutions (ici une seule pour le moment). La colonne de gauche indique le nombre de passages sur chaque ligne. Les lignes contenant un tiret &laquo;&nbsp;<code>-</code>&nbsp;&raquo; ne correspondent à aucun code compilé. Nous voyons que les lignes 3, 5, 6 et 12 ont été parcourues à trois reprises (une invocation pour chaque argument de la ligne de commande), et les lignes 9, 10, et 11 ont exécutées 12 fois (les itérations pour calculer les factorielles).</p>
<p style="text-align: justify;">Si nous réitérons l&#8217;opération, les compteurs se cumulent.</p>
<pre>$ <strong>test/calcule-factorielle 3 8</strong>
3! = 6
8! = 40320
[factorielle]$ <strong>gcov -o src/ fact.c</strong>
File './src/fact.c'
Lignes exécutées: 87.50% de 8
./src/fact.c:creating 'fact.c.gcov'

[factorielle]$ <strong>test/calcule-factorielle 3 8</strong>
3! = 6
8! = 40320
[factorielle]$ <strong>cat fact.c.gcov </strong>
        -:    0:Source:./src/fact.c
        -:    0:Graph:src/fact.gcno
        -:    0:Data:src/fact.gcda
        -:    0:Runs:2
        -:    0:Programs:1
        -:    1:#include
        -:    2:
        5:    3:int factorielle(long int n, long long int * result)
        -:    4:{
        5:    5:	* result = 1;
        5:    6:	if (n &lt; 0)
    #####:    7:		return -1;
        -:    8:	do {
       21:    9:		(*result) = (*result) * n;
       21:   10:		n = n - 1;
       21:   11:	} while (n &gt; 1);
        5:   12:	return 0;
        -:   13:}
        -:   14:
[factorielle]$</pre>
<p style="text-align: justify;">Et la ligne 7 ? Pourquoi ces &laquo;&nbsp;<code>#####</code>&nbsp;&raquo; ?</p>
<p style="text-align: justify;">Contrairement aux tableurs courants, ce symbole ne signifie pas que le nombre est trop grand pour tenir dans la colonne, mais que la ligne (qui correspond bien à du code compilé) n&#8217;a jamais été exécutée. Deux intérêts à cette notation :</p>
<ul>
<li style="text-align: justify;">attirer l&#8217;oeil lors du parcours du listing plutôt qu&#8217;un simple &laquo;&nbsp;<code>0</code>&nbsp;&raquo; ;</li>
<li style="text-align: justify;">permettre une recherche automatisée des lignes non exécutées à l&#8217;aide de <code>grep</code>.</li>
</ul>
<p style="text-align: justify;">Voyons :</p>
<pre>[factorielle]$ <strong>gcov -o src/ fact.c</strong>
File './src/fact.c'
Lignes exécutées: 87.50% de 8
./src/fact.c:creating 'fact.c.gcov'

[factorielle]$ <strong>grep "#####" fact.c.gcov</strong>
    #####:    7:		return -1;
[factorielle]$</pre>
<p style="text-align: justify;">Le numéro de ligne (7) étant affiché ainsi que son contenu, cela permet un aperçu rapide du code non parcouru.</p>
<h1>Correction</h1>
<p style="text-align: justify;">Nous avons détecté un problème de test de notre bibliothèque, puisqu&#8217;une branche n&#8217;a jamais été exécutée. Ceci peut être dû à plusieurs raisons.</p>
<ul>
<li style="text-align: justify;">Un jeu de test incomplet. C&#8217;est le cas ici et nous allons y remédier facilement ci-dessous.</li>
<li style="text-align: justify;">Du code ancien qui n&#8217;est plus jamais appelé (code mort). Il est important de le faire disparaître car il perturbe la maintenance du programme.</li>
<li style="text-align: justify;">Des lignes de code qui servent à gérer des cas d&#8217;erreur rares, difficiles à tester. Nous y reviendrons dans le prochain paragraphe.</li>
</ul>
<p style="text-align: justify;">Ici la solution est simple, la ligne non testée correspond à une invocation de la fonction avec un argument négatif. C&#8217;est facile à produire.</p>
<pre>[factorielle]$ <strong>test/calcule-factorielle -1</strong>
-1! n'existe pas
[factorielle]$ <strong>gcov -o src/ fact.c</strong>
File './src/fact.c'
Lignes exécutées: 100.00% de 8
./src/fact.c:creating 'fact.c.gcov'

[factorielle]$</pre>
<p style="text-align: justify;">A présent, <code>gcov</code> nous indique que toutes les lignes de notre programme ont été couvertes par notre jeu de test. Cela réduit la probabilité de bug restant.</p>
<h1>Traitement d&#8217;erreur</h1>
<p style="text-align: justify;">Une grosse difficulté pour assurer une couverture de code à 100% lors des tests d&#8217;un logiciel est de valider les comportements en cas d&#8217;erreur système.</p>
<p style="text-align: justify;">Prenons le cas d&#8217;un appel-système classique : <code>malloc()</code>. On lui demande de nous allouer une zone mémoire d&#8217;une certaine taille (précisée en octets) et il nous renvoie un pointeur. Toutes les documentations précisent qu&#8217;en cas de manque de mémoire, <code>malloc()</code> renvoie un pointeur <code>NULL</code>. (Bien qu&#8217;en pratique sous Linux ce soit particulièrement difficile à produire, nous en reparlerons dans un futur article).<br />
Aussi, le programmeur consciencieux écrira-t-il quelque chose comme.</p>
<pre>    char * buffer;
    buffer = malloc(TAILLE_BUFFER);
    if (buffer == NULL) {
        signaler_erreur("Manque de memoire");
        enregistrer_code_d_erreur(-ENOMEM);
        return -1;
    }
    // ...</pre>
<p style="text-align: justify;">Malheureusement les lignes de la portion entre accolades sont difficiles à tester car on ne peut pas &laquo;&nbsp;forcer&nbsp;&raquo; <code>malloc()</code> à échouer ; les circonstances reposent sur trop de paramètres externes à l&#8217;application pour être réellement reproductible.</p>
<p style="padding-left: 60px; text-align: justify;">Dans le cas précis de <code>malloc()</code>, la GlibC nous offre des points d&#8217;entrées que l&#8217;on peut utiliser pour remplacer la fonction &#8211; voir <code>malloc_hook(3)</code> &#8211; mais ça n&#8217;est pas souvent le cas avec les appels-système.</p>
<p style="text-align: justify;">Il existe néanmoins plusieurs solutions. L&#8217;une d&#8217;elle, que j&#8217;ai utilisée plusieurs fois, consiste à employer une couche logicielle minimale qui reproduit les appels-système dont nous avons besoin en simulant un échec si certains critères sont remplis. Par exemple la routine suivante reproduit <code>malloc()</code> mais échoue au bout d&#8217;un nombre d&#8217;invocations contenu dans la variable d&#8217;environnement <code>MALLOC_FAIL</code>.</p>
<pre><strong>src/my_malloc.c:</strong>

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

void * my_malloc(size_t length)
{
	char * string;
	char buffer[32];
	int count;
	string = getenv ("MALLOC_FAIL");
	if (string != NULL) {
		if (sscanf(string, "%d", &amp; count)  == 1) {
			count --;
			if (count == 0)
				return NULL;
			snprintf(buffer, 80, "%d", count);
			setenv("MALLOC_FAIL", buffer, 1 );
		}
	}
	return malloc(length);
}</pre>
<p style="text-align: justify;">On peut la déclarer dans un fichier d&#8217;en-tête ainsi:</p>
<pre><strong>include/my_malloc.h:</strong>

#ifndef MY_MALLOC_H
#define MY_MALLOC_H

#ifndef NDEBUG
        extern void * my_malloc(size_t);
#else
#define my_malloc(L) malloc(L)
#endif

#endif</pre>
<p style="text-align: justify;">De cette manière, suivant la présence ou non de la constante <code>NDEBUG</code>, qui représente traditionnellement la compilation en version &laquo;&nbsp;production&nbsp;&raquo; pour la bibliothèque C, notre routine sera compilée comme le <code>malloc()</code> habituel ou avec notre gestion de la variable d&#8217;environnement.</p>
<p style="text-align: justify;">Compilons une bibliothèque dynamique avec notre couche d&#8217;abstraction minimale.</p>
<pre>[factorielle]$ <strong>gcc -c -fPIC -Wall -I include/ -o src/my_malloc.o src/my_malloc.c </strong>
[factorielle]$ <strong>gcc -shared -Wl,-soname,libmytest.so.1 -o lib/libmytest.so.1.0 src/my_malloc.o</strong>
[factorielle]$ <strong>ldconfig -n lib/</strong>
[factorielle]$ <strong>ln -sf libmytest.so.1 lib/libmytest.so</strong>
[factorielle]$ <strong>ls -l lib/libmy*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-02-12 04:12 lib/libmytest.so -&gt; libmytest.so.1
lrwxrwxrwx 1 cpb cpb   16 2012-02-12 04:11 lib/libmytest.so.1 -&gt; libmytest.so.1.0
-rwxrwxr-x 1 cpb cpb 7014 2012-02-12 04:11 lib/libmytest.so.1.0
[factorielle]$</pre>
<p style="text-align: justify;">Créons un petit executable qui utilise notre bibliothèque de test en bouclant autour du <code>my_malloc()</code></p>
<pre><strong>test/test-mymalloc.c:</strong>

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;my_malloc.h&gt;

int main(void)
{
        int i = 1;
        while(1) {
                fprintf(stderr, "i = %2d...", i);
                if (<strong>my_malloc</strong>(10) == NULL) {
                        fprintf(stderr, "echec !\n");
                        break;
                }
                fprintf(stderr, "ok\n");
                i ++;
        }
        return 0;
}</pre>
<p style="text-align: justify;">Compilation&#8230;</p>
<pre>[factorielle]$ <strong>gcc -I include/ -L lib/ -Wall -o test/test-mymalloc test/test-mymalloc.c -lmytest</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Et premier test, sans définir la variable d&#8217;environnement.</p>
<pre>[factorielle]$ <strong>unset MALLOC_FAIL</strong>
[factorielle]$ <strong>./test/test-mymalloc </strong>
i =  1...ok
i =  2...ok
i =  3...ok
i =  4...ok
i =  5...ok
[...]
i = 21928...ok
i = 21929...ok
i = 21930...ok
i = 21931...ok
i = 21932...
  (<strong><em>Contrôle-C</em></strong>)
[factorielle]$</pre>
<p style="text-align: justify;">Bien entendu, notre programme ne s&#8217;arrête pas. Ou plutôt il s&#8217;arrêtera au bout d&#8217;un long moment après avoir épuisé ses 3Go d&#8217;espace d&#8217;adressage (sur une machine 32 bits).</p>
<p style="text-align: justify;">Ré-essayons en forçant un échec au quatrième appel de <code>malloc()</code>.</p>
<pre>[factorielle]$ <strong>export MALLOC_FAIL=4</strong>
[factorielle]$ <strong>./test/test-mymalloc</strong>
i =  1...ok
i =  2...ok
i =  3...ok
i =  4...echec !
[factorielle]$</pre>
<p style="text-align: justify;">Naturellement, ce principe consistant à faire échouer les appels aux fonctions système sous le contrôle d&#8217;une variable d&#8217;environnement &#8211; ou d&#8217;autres paramètres (variable globale, fichier, zone de mémoire partagée, etc.) &#8211; peut s&#8217;appliquer autant au contenu d&#8217;une bibliothèque qu&#8217;à celui d&#8217;un exécutable lorsqu&#8217;on a besoin d&#8217;assurer une couverture de code de 100% sur l&#8217;ensemble du jeu de tests d&#8217;une application.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">Nous avons observé dans cette petite série d&#8217;articles, comment créer, déboguer et tester une bibliothèque dynamique. Je vous encourage à faire vos propres essais, en vous reportant à la documentation de <code>gcc</code>, <code>gdb</code>, <code>gcov</code>, mais également d&#8217;autres outils complémentaires comme <code>gprof</code>, <code>ldconfig</code>, <code>valgrind</code>, etc.</p>
<p style="text-align: center;">Tous les commentaires, remarques, corrections, etc. sont les bienvenus.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/12/mise-au-point-de-bibliotheque-dynamique-33/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Development of a dynamic library (3/3)</title>
		<link>http://www.blaess.fr/christophe/2012/02/12/development-of-a-dynamic-library-33/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/12/development-of-a-dynamic-library-33/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 08:59:07 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1655</guid>
		<description><![CDATA[(version originale en français ici) In the two previous posts, we started the development of a dynamic library on Linux: the first one saw us building the library and managing version numbers using symbolic links, in the second one we traced library calls and did step-by-step debugging. Now we are interested in checking the coverage [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(version originale en français <a title="Mise au point de bibliothèque dynamique (3/3)" href="http://www.blaess.fr/christophe/2012/02/12/mise-au-point-de-bibliotheque-dynamique-33/">ici</a>)</p>
<p style="text-align: justify;">In the two previous posts, we started the development of a dynamic library on Linux: the <a title="Development of a dynamic library (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/" target="_blank">first one</a> saw us building the library and managing version numbers using symbolic links, in the <a title="Development of a dynamic library (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/" target="_blank">second one</a> we traced library calls and did step-by-step debugging. Now we are interested in checking the coverage of the library.</p>
<p style="padding-left: 60px; text-align: justify;"><strong><em>Code coverage</em></strong> is a measure indicating the percentage of lines of code that were actually covered during a program execution. We can gradually expand the set of tests to obtain 100% coverage (and ensure that the code has been fully verified).</p>
<p style="text-align: justify;">The best known tool under Linux for code coverage is <code>gcov</code>, which instruments the source code and provide us detailed statistics after execution. It is easy to use it to verify coverage of a source file compiled into an executable. We will use it for library code which requires some more attention.</p>
<h1>Compilation</h1>
<p style="text-align: justify;">Let&#8217;s start with the same files and directories (grouped in <a title="http://www.blaess.fr/christophe/files/article-2012-02-12/factorial-3.tar.bz2" href="http://www.blaess.fr/christophe/files/article-2012-02-12/factorial-3.tar.bz2" target="_blank">this archive</a>) as in the previous articles. A directory named &laquo;&nbsp;<code>factorial</code>&nbsp;&raquo; contains four subdirectories: <code>src</code>, <code>include</code> and <code>lib</code> respectively where the source files, header files and compiled files of the library are. The fourth sub-directory &laquo;&nbsp;<code>test</code>&nbsp;&raquo; contains the source and executable files of a program using our library <code>libfact</code>.</p>
<pre>[~]$ <strong>cd factorial/</strong>
[factorial]$ <strong>ls</strong>
include  lib  src  test
[factorial]$ <strong>ls include/</strong>
fact.h
[factorial]$ <strong>ls lib/</strong>
libfact.so  libfact.so.2  libfact.so.2.0
[factorial]$ <strong>ls src/</strong>
fact.c
[factorial]$ <strong>ls test/</strong>
factorial.c
[factorial]$</pre>
<p style="padding-left: 60px; text-align: justify;">NB: The library was already compiled in the above example, but we will rebuild it.</p>
<p style="text-align: justify;">At first we will compile the library code, as in the first article, but adding the <code>--coverage</code> option of <code>gcc</code>. This option has two different roles:</p>
<ul>
<li style="text-align: justify;">During compilation, it has the same meaning as <code>-fprofile-arcs</code> and <code>-ftest-coverage</code> options (which were used with the previous versions of <code>gcc</code>): the first one added instrumentation data to the executable code (counters), the second one created a table of correspondence between instructions blocks and lines of source code (in a file named after the source file with the extension <code>.gcno</code>)</li>
<li style="text-align: justify;">During linking, it is equivalent to <code>-lgcov</code> (which was added automatically by <code>-ftest-coverage</code>) that incorporates the necessary entry points for the subsequent use of <code>gcov</code>.</li>
</ul>
<p style="text-align: justify;">Here&#8217;s the compilation of our library.</p>
<pre>[factorial]$ <strong>gcc -c --coverage -fPIC -I include/ -o ./src/fact.o ./src/fact.c </strong>
[factorial]$ <strong>ls src/</strong>
fact.c  fact.gcno  fact.o
[factorial]$</pre>
<p style="text-align: justify;">We see that with the <code>--coverage</code> option, the compilation generated, in addition to the <code>fact.o</code> object file, a <code>fact.gcno</code> file, containing the relationships between the blocks of code and the line numbers. We continue.</p>
<pre>[factorial]$ <strong>gcc -shared -I include/ -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 ./src/fact.o --coverage </strong>
[factorial]$ <strong>ls -l lib/</strong>
total 20
lrwxrwxrwx 1 cpb cpb    12 2012-02-11 13:39 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb    14 2012-02-11 13:39 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 16866 2012-02-11 17:10 libfact.so.2.0
[factorial]$</pre>
<p style="text-align: justify;">We have rebuilt the <code>libfact.so.2.0</code> library. Symbolic links are used to manage the major and minor version numbers, as we saw in the <a title="Development of a dynamic library (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/">first article</a>. Now compile an executable file, without <code>--coverage</code> option (or use the executable file of previous articles).</p>
<pre>[factorial]$ <strong>gcc -I include/ -L lib/ -o test/factorial test/factorial.c -lfact</strong>
[factorial]$ <strong>ls test/</strong>
factorial  factorial.c
[factorial]$</pre>
<h1>Execution</h1>
<p style="text-align: justify;">Program execution takes place quite normally (although in practice it is slightly slower). We must, of course, set the environment variable <code>LD_LIBRARY_PATH</code> to specify where the dynamic linker will find the library needed to run the application.</p>
<pre>[factorial]$ <strong>export LD_LIBRARY_PATH=lib/</strong>
[factorial]$ <strong>test/factorial 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorial]$ <strong>ls src/</strong>
fact.c  fact.gcda  fact.gcno  fact.o
[factorial]$</pre>
<p style="text-align: justify;">A new file named <code>fact.gcda</code> appeared, containing the execution statistics for <code>fact.gcno</code> blocks of code (and block transitions).</p>
<h1>Results</h1>
<p style="text-align: justify;">For information on the code coverage of a source file, we invoke <code>gcov</code> indicating the source file name. The results are computed independently for each source file of the application (or library).</p>
<p style="text-align: justify;">We will use the <code>-o</code> option of <code>gcov</code> to specify the directory name for the <code>.c</code>, <code>.gcno</code> and <code>.gcda</code> files.</p>
<pre>[factorial]$ <strong>gcov -o src/ fact.c</strong>
File 'src/fact.c'
Lines executed:87.50% of 8
src/fact.c:creating 'fact.c.gcov'

[factorielle]$</pre>
<p style="text-align: justify;"><code>gcov</code> tells us that we have only performed 87.5% of the eight lines of code in our function. What happened? We see that <code>gcov</code> also created a file named &laquo;&nbsp;<code>fact.c.gcov</code>&nbsp;&raquo; in which he puts a copy of our source code, numbering the lines, adding a header and a column of statistics at the beggining of the line.</p>
<pre>[factorial]$ <strong>ls</strong>
fact.c.gcov  include  lib  src  test
[factorial]$ <strong>cat fact.c.gcov </strong>
        -:    0:Source:src/fact.c
        -:    0:Graph:src/fact.gcno
        -:    0:Data:src/fact.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include
        -:    2:
        3:    3:int factorial(long int n, long long int * result)
        -:    4:{
        3:    5:    * result = 1;
        3:    6:    if (n &lt; 0)
    #####:    7:        return -1;
        -:    8:    do {
       12:    9:        (*result) = (*result) * n;
       12:   10:        n = n - 1;
       12:   11:    } while (n &gt; 1);
        3:   12:    return 0;
        -:   13:}
        -:   14:
[factorial]$</pre>
<p style="text-align: justify;">The header describes the files involved and the number of executions (only one here). The left column shows the number of passes on each line. Lines containing a dash &laquo;&nbsp;-&nbsp;&raquo; do not match any compiled code. We see that lines 3, 5, 6 and 12 were scanned three times (one invocation for each argument on the command line), and that lines 9, 10 and 11 were executed 12 times (iterations to calculate factorial).</p>
<p style="text-align: justify;">If we repeat the operation, the counters are cumulated.</p>
<pre>[factorial]$ <strong>test/factorial 3 8</strong>
3! = 6
8! = 40320
[factorial]$ <strong>gcov -o src/ fact.c</strong>
File 'src/fact.c'
Lines executed:87.50% of 8
src/fact.c:creating 'fact.c.gcov'

[factorial]$ <strong>cat fact.c.gcov </strong>
        -:    0:Source:src/fact.c
        -:    0:Graph:src/fact.gcno
        -:    0:Data:src/fact.gcda
        -:    0:Runs:2
        -:    0:Programs:1
        -:    1:#include
        -:    2:
        5:    3:int factorial(long int n, long long int * result)
        -:    4:{
        5:    5:    * result = 1;
        5:    6:    if (n &lt; 0)
    #####:    7:        return -1;
        -:    8:    do {
       21:    9:        (*result) = (*result) * n;
       21:   10:        n = n - 1;
       21:   11:    } while (n &gt; 1);
        5:   12:    return 0;
        -:   13:}
        -:   14:
[factorial]$</pre>
<p style="text-align: justify;">But, what about line 7? Why these &laquo;&nbsp;<code>#####</code>&laquo;&nbsp;?</p>
<p style="text-align: justify;">Unlike spreadsheet programs, this symbol does not mean that the number is too large to fit in the column, but that the line (which corresponds to compiled code) was never executed. Two advantages with this notation:</p>
<ul>
<li style="text-align: justify;">it attracts the eye better than a single &laquo;&nbsp;0&#8243; would do,</li>
<li style="text-align: justify;">it allows us to do an automated search of unexecuted lines using <code>grep</code>.</li>
</ul>
<p style="text-align: justify;">Let&#8217;s see:</p>
<pre>[factorial]$ <strong>gcov -o src fact.c</strong>
File 'src/fact.c'
Lines executed:87.50% of 8
src/fact.c:creating 'fact.c.gcov'

[factorial]$ <strong>grep '#####' fact.c.gcov </strong>
    #####:    7:        return -1;
[factorial]$</pre>
<p style="text-align: justify;">The line number (7) being printed with the content, we can have a quick overview of non executed code.</p>
<h1>Correction</h1>
<p style="text-align: justify;">We have detected a problem with our test library, since a branch was never executed. This may be due to different reasons.</p>
<ul>
<li style="text-align: justify;">An incomplete tests set. This is the case here and we will fix it easily below.</li>
<li style="text-align: justify;">Legacy code that is never invoked again (dead code). It is important to make it disappear because it disrupts the maintenance of the program.</li>
<li>Lines of code used to handle rare error cases, difficult to test. We will deal with this problem in the next paragraph.</li>
</ul>
<p style="text-align: justify;">Here the solution is simple: the uncovered line corresponds to an invocation of the function with a negative argument. It is easy to test.</p>
<pre>[factorial]$ <strong>./test/factorial -1</strong>
-1! doesn't exist
[factorial]$ <strong>gcov -o src fact.c</strong>
File 'src/fact.c'
Lines executed:100.00% of 8
src/fact.c:creating 'fact.c.gcov'

[factorial]$</pre>
<p style="text-align: justify;">Now <code>gcov</code> tells us that all the lines of our program have been covered by our test set. This reduces the probability of remaining bug.</p>
<h1>Error handling</h1>
<p style="text-align: justify;">A major difficulty to achieve 100% code coverage during software testing is to validate the behavior in case of system error.</p>
<p style="text-align: justify;">Take a look at a well-known system call: <code>malloc()</code>. We asked him to allocate a memory area of ​​a certain size (given in bytes) and he returns a pointer. All documentation tell you that in case of lack of memory, <code>malloc()</code> returns a <code>NULL</code> pointer. (Although this case is particularly difficult to produce with Linux, we will discuss this in a future article). Also, the conscientious programmer will write something like.</p>
<pre>    char * buffer;
    buffer = malloc(BUFFER_SIZE);
    if (buffer == NULL) {
        display_error("Insufficient memory");
        register_error_code(-ENOMEM);
        return -1;
    }
    // ...</pre>
<p style="text-align: justify;">Unfortunately the lines in between braces are difficult to test because we can not &laquo;&nbsp;force&nbsp;&raquo; <code>malloc()</code> to fail. The failure circumstances are based on too many parameters external to the application to be reproducible.</p>
<p style="padding-left: 60px; text-align: justify;">In the specific case of <code>malloc()</code>, the <code>Glibc</code> library provides us entry points that can be used to replace the function &#8211; see <code>malloc_hook(3)</code> &#8211; but it is not possible for other system calls.</p>
<p style="text-align: justify;">However there are several solutions. One of them, I have used several times, is to use a software layer that replicates the minimum system calls we need and simulates a failure if certain criteria are met. For example the following routine reproduces <code>malloc()</code> but fails after a number of invocations contained in the environment variable <code>MALLOC_FAIL</code>.</p>
<pre><strong>src/my_malloc.c:</strong>

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

void * my_malloc(size_t length)
{
	char * string;
	char buffer[32];
	int count;
	string = getenv ("MALLOC_FAIL");
	if (string != NULL) {
		if (sscanf(string, "%d", &amp; count)  == 1) {
			count --;
			if (count == 0)
				return NULL;
			snprintf(buffer, 80, "%d", count);
			setenv("MALLOC_FAIL", buffer, 1 );
		}
	}
	return malloc(length);
}</pre>
<p style="text-align: justify;">We can declare it in a header file as follows:</p>
<pre><strong>include/my_malloc.h:</strong>

#ifndef MY_MALLOC_H
#define MY_MALLOC_H

#ifndef NDEBUG
        extern void * my_malloc(size_t);
#else
#define my_malloc(L) malloc(L)
#endif

#endif</pre>
<p style="text-align: justify;">In this way, depending on the presence or absence of the <code>NDEBUG</code> constant, which traditionally represents for the C library the production version of the code, our routine will be compiled as usual <code>malloc()</code> or with our management of the environment variable.</p>
<p style="text-align: justify;">Compile a dynamic library with our minimal abstraction layer minimum.</p>
<pre>[factorial]$ <strong>gcc -c -fPIC -Wall -I include/ -o src/my_malloc.o src/my_malloc.c </strong>
[factorial]$ <strong>gcc -shared -Wl,-soname,libmytest.so.1 -o lib/libmytest.so.1.0 src/my_malloc.o</strong>
[factorial]$ <strong>ldconfig -n lib/</strong>
[factorial]$ <strong>ln -sf libmytest.so.1 lib/libmytest.so</strong>
[factorial]$ <strong>ls -l lib/libmy*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-02-12 04:12 lib/libmytest.so -&gt; libmytest.so.1
lrwxrwxrwx 1 cpb cpb   16 2012-02-12 04:11 lib/libmytest.so.1 -&gt; libmytest.so.1.0
-rwxrwxr-x 1 cpb cpb 7014 2012-02-12 04:11 lib/libmytest.so.1.0
[factorial]$</pre>
<p style="text-align: justify;">Let&#8217;s write a small program that uses our test library by looping around <code>my_malloc()</code>.</p>
<pre><strong>test/test-mymalloc.c:</strong>

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;my_malloc.h&gt;

int main(void)
{
        int i = 1;
        while(1) {
                fprintf(stderr, "i = %2d...", i);
                if (<strong>my_malloc</strong>(10) == NULL) {
                        fprintf(stderr, "failure!\n");
                        break;
                }
                fprintf(stderr, "ok\n");
                i ++;
        }
        return 0;
}</pre>
<p style="text-align: justify;">Compilation&#8230;</p>
<pre>[factorial]$ <strong>gcc -I include/ -L lib/ -Wall -o test/test-mymalloc test/test-mymalloc.c -lmytest</strong>
[factorial]$</pre>
<p style="text-align: justify;">First test, without the environment variable.</p>
<pre>[factorial]$ <strong>unset MALLOC_FAIL</strong>
[factorial]$ <strong>./test/test-mymalloc </strong>
i =  1...ok
i =  2...ok
i =  3...ok
i =  4...ok
i =  5...ok
[...]
i = 21928...ok
i = 21929...ok
i = 21930...ok
i = 21931...ok
i = 21932...
  (<strong><em>Control-C</em></strong>)
[factorial]$</pre>
<p style="text-align: justify;">Of course, our program does not stop. Or rather it will stop after a long runtime when exhausting its 3GB address space (on a 32-bits machine).</p>
<p style="text-align: justify;">Try again by forcing a failure in the fourth <code>malloc()</code> call.</p>
<pre>[factorial]$ <strong>export MALLOC_FAIL=4</strong>
[factorial]$ <strong>./test/test-mymalloc</strong>
i =  1...ok
i =  2...ok
i =  3...ok
i =  4...failure!
[factorial]$</pre>
<p style="text-align: justify;">Of course, this principle of forcing system call failures under the control of an environment variable &#8211; or other parameters (global variable, file, shared memory area, etc.) &#8211; can be applied equally to a library code when you need 100% code coverage over the entire set of tests for an application.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">We observed in this small series of articles, how to create, debug and test a dynamic library. I encourage you to do your own tests, referring to the documentation of <code>gcc</code>, <code>gdb</code>, <code>gcov</code>, but also other complementary tools such as <code>gprof</code>, <code>ldconfig</code>, <code>valgrind</code>, etc.</p>
<p style="text-align: center;">All comments, remarks, corrections, etc.. are welcome.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/12/development-of-a-dynamic-library-33/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Version Control by Example</title>
		<link>http://www.blaess.fr/christophe/2012/02/08/version-control-by-example/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/08/version-control-by-example/#comments</comments>
		<pubDate>Wed, 08 Feb 2012 07:30:12 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Livres]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1578</guid>
		<description><![CDATA[J&#8217;ai reçu il y a quelques jours un exemplaire gratuit du livre &#171;&#160;Version Control by Example&#160;&#187; d&#8217;Eric Sink. &#160; Il s&#8217;agit d&#8217;un ouvrage très intéressant sur le fonctionnement des logiciels de gestion de version avec une présentation détaillée des différentes générations (gestion localisée, gestion centralisée, gestion distribuée) et des outils correspondant (RCS, CVS, Subversion, Mercurial [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.ericsink.com/vcbe/index.html"><img class="alignleft size-full wp-image-1579" title="version-control-by-example" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/02/version-control-by-example.jpg" alt="" width="240" height="315" /></a>J&#8217;ai reçu il y a quelques jours un exemplaire gratuit du livre &laquo;&nbsp;<em>Version Control by Example</em>&nbsp;&raquo; d&#8217;Eric Sink.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">Il s&#8217;agit d&#8217;un ouvrage très intéressant sur le fonctionnement des logiciels de gestion de version avec une présentation détaillée des différentes générations (gestion localisée, gestion centralisée, gestion distribuée) et des outils correspondant (RCS, CVS, Subversion, Mercurial Git, Veracity&#8230;).</p>
<p>&nbsp;</p>
<p style="text-align: justify;">J&#8217;ai appris plusieurs détails sur Git et sur les commandes auxquelles je n&#8217;étais pas habitué et je vous le conseille chaudement.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">Le livre est disponible en version PDF sur le <a title="Eric Sink's Blog" href="http://www.ericsink.com/index.html" target="_blank">blog de l&#8217;auteur</a> et il offre même des exemplaires gratuits sur demande. Naturellement on peut aussi l&#8217;acheter chez <a title="Version Control by Example" href="http://www.amazon.com/Version-Control-Example-Eric-Sink/dp/0983507902/ref=sr_1_1?ie=UTF8&amp;qid=1328865261&amp;sr=8-1" target="_blank">les revendeurs habituels</a>.</p>
<p style="text-align: center;">Ainsi que son précédent ouvrage :<br />
<iframe style="width: 120px; height: 240px;" src="http://rcm-fr.amazon.fr/e/cm?t=chbl-21&amp;o=8&amp;p=8&amp;l=as1&amp;asins=1590596234&amp;IS1=1&amp;ref=qf_sp_asin_til&amp;fc1=000000&amp;lt1=_blank&amp;m=amazon&amp;lc1=5375BA&amp;bc1=F7F4EB&amp;bg1=F7F4EB&amp;f=ifr" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="320" height="240"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/08/version-control-by-example/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dark Side of the Moon</title>
		<link>http://www.blaess.fr/christophe/2012/02/05/dark-side-of-the-moon/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/05/dark-side-of-the-moon/#comments</comments>
		<pubDate>Sun, 05 Feb 2012 09:32:26 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Intérêts divers]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1558</guid>
		<description><![CDATA[J&#8217;ai eu la chance d&#8217;assister vendredi dernier à un excellent spectacle musical nommé &#171;&#160;La face cachée de la Lune&#160;&#187; que je conseille sans la moindre hésitation à tous les fans des Pink Floyd si d&#8217;autres dates sont programmées. &#160; L&#8217;équipe de Thierry Balasse reconstruit en direct devant nous l&#8217;album mythique des Floyd, dans ses moindres [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-1559" title="Dark-side-of-the-moon" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/02/Dark-side-of-the-moon.jpg" alt="" width="320" height="229" /></p>
<p style="text-align: justify;">J&#8217;ai eu la chance d&#8217;assister vendredi dernier à un excellent spectacle musical nommé &laquo;&nbsp;<em>La face cachée de la Lune</em>&nbsp;&raquo; que je conseille sans la moindre hésitation à tous les fans des Pink Floyd si d&#8217;autres dates sont programmées.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">L&#8217;équipe de <a title="Page Facebook de Thierry Balasse" href="http://www.facebook.com/inouie" target="_blank">Thierry Balasse</a> reconstruit en direct devant nous l&#8217;album mythique des Floyd, dans ses moindres détails, y compris (et surtout) les bruitages, les vocalises, et les parties instrumentales aux synthétiseurs analogiques. Il ne s&#8217;agit pas d&#8217;un concert habituel où l&#8217;on joue une version de l&#8217;album arrangée pour la scène, mais bien de la reconstruction de la version studio.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">Pour être bien clair : aucune bande pré-enregistrée n&#8217;est utilisée, tout est joué ou créé sur place. Les voix et les pas de course de &laquo;&nbsp;<em>On the Run</em>&nbsp;&raquo; , les carillons de &laquo;&nbsp;<em>Time</em>&nbsp;&raquo; , ou la caisse enregistreuse de &laquo;&nbsp;<em>Money</em>&nbsp;&raquo; sont sur scène. Pas moins de dix musiciens sont présents pour la re-création de cet album, avec des moments de grâce absolue (comme la partition vocale de &laquo;&nbsp;<em>The Great Gig in the Sky</em>&nbsp;&raquo; !). Des petites parenthèses en provenance de l&#8217;album &laquo;&nbsp;<em>Meddle</em>&nbsp;&raquo; sont adroitement intercalées (&laquo;&nbsp;<em>One of these Days</em>&nbsp;&raquo; , &laquo;&nbsp;<em>Echoes</em>&nbsp;&raquo; &#8230;). J&#8217;ai été particulièrement sensible à la présence des synthétiseurs analogiques d&#8217;époque (<em>Minimoog</em>, <em>VCS3</em>, etc.).</p>
<p>Un spectacle qui, je l&#8217;espère, se poursuivra avec succès.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/05/dark-side-of-the-moon/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mise au point de bibliothèque dynamique (2/3)</title>
		<link>http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/#comments</comments>
		<pubDate>Sat, 04 Feb 2012 09:00:38 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1540</guid>
		<description><![CDATA[(English translation) Nous avons examiné dans l&#8217;article précédent comment compiler une bibliothèque dynamique et gérer correctement ses numéros majeurs et mineurs de version afin d&#8217;en faciliter la maintenance, tant pour le développeur (de la bibliothèque mais aussi pour celui des applications qui l&#8217;utilisent) que pour l&#8217;administrateur du système sur lequel elle est installée. Nous allons [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(<a title="Development of a dynamic library (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/">English translation</a>)</p>
<p style="text-align: justify;">
<p style="text-align: justify;">Nous avons examiné dans l&#8217;<a title="Mise au point de bibliothèque dynamique (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/" target="_blank">article précédent</a> comment compiler une bibliothèque dynamique et gérer correctement ses numéros majeurs et mineurs de version afin d&#8217;en faciliter la maintenance, tant pour le développeur (de la bibliothèque mais aussi pour celui des applications qui l&#8217;utilisent) que pour l&#8217;administrateur du système sur lequel elle est installée. Nous allons à présent examiner comment effectuer le débogage de notre bibiliothèque et des applications qui l&#8217;appellent.</p>
<h1>Suivi des appels</h1>
<p style="text-align: justify;">Le premier utilitaire a connaître est <code><strong>ltrace</strong></code>. Il permet d&#8217;envoyer vers la sortie d&#8217;erreur du processus une trace de tous les appels de fonctions de bibliothèques dynamiques. Nous nous replaçons dans la même situation que pour l&#8217;article précédent avec les répertoires suivants</p>
<ul>
<li style="text-align: justify;"><code>factorielle/src</code> contient le code source de la bibliothèque</li>
<li style="text-align: justify;"><code>factorielle/include</code> où se trouvent les fichiers d&#8217;en-tête de la bibliothèque</li>
<li style="text-align: justify;"><code>factorielle/lib</code> contenant la bibliothèque compilée</li>
<li style="text-align: justify;"><code>factorielle/test</code> dans lequel on trouve codes sources et fichiers exécutables des applications appelant la bibliothèque</li>
</ul>
<p style="text-align: justify;">Voici un aperçu du contenu de notre répertoire</p>
<pre>[~] <strong>cd factorielle</strong>
[factorielle]$ <strong>ls</strong>
include  lib  src  test
[factorielle]$ <strong>ls include/</strong>
<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.h" target="_blank">fact.h</a>
[factorielle]$ <strong>ls -l lib/</strong>
total 8
lrwxrwxrwx 1 cpb cpb   12 2012-02-04 05:04 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb   14 2012-02-04 05:04 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-02-04 05:04 libfact.so.2.0
[factorielle]$ <strong>ls src/</strong>
<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.c" href="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.c" target="_blank">fact.c</a>  fact.o
[factorielle]$ <strong>ls test/</strong>
calcule-factorielle  <a title="http://www.blaess.fr/christophe/files/article-2012-02-04/calcule-factorielle.c" href="http://www.blaess.fr/christophe/files/article-2012-02-04/calcule-factorielle.c" target="_blank">calcule-factorielle.c</a>
[factorielle]$</pre>
<p style="text-align: justify;">Les détails pour créer les fichiers exécutables et les liens symboliques se trouvaient dans <a title="Mise au point de bibliothèque dynamique (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/" target="_blank">le précédent article</a>. Voici également un exemple d&#8217;exécution du programme de test.</p>
<pre>[factorielle]$ <strong>export LD_LIBRARY_PATH=lib/</strong>
[factorielle]$ <strong>./test/calcule-factorielle 7</strong>
7! = 5040
[factorielle]$</pre>
<p style="text-align: justify;">Employons <code>ltrace</code> pour observer les appels de fonctions de bibliothèques.</p>
<pre>[factorielle]$ <strong>ltrace ./test/calcule-factorielle 4 5 6</strong>
__libc_start_main(0x80485b4, 4, 0xbf896624, 0x80486b0, 0x8048720
__isoc99_sscanf(0xbf89745f, 0x8048796, 0xbf896578, 0x80486d1, 0x8048500) = 1
factorielle(4, 0xbf896570, 0xbf896578, 0x80486d1, 0x8048500) = 0
fprintf(0xb7896500, "%ld! = %lld\n", 4, ...4! = 24
)     = 8
__isoc99_sscanf(0xbf897461, 0x8048796, 0xbf896578, 24, 0) = 1
factorielle(5, 0xbf896570, 0xbf896578, 24, 0)    = 0
fprintf(0xb7896500, "%ld! = %lld\n", 5, ...5! = 120
)     = 9
__isoc99_sscanf(0xbf897463, 0x8048796, 0xbf896578, 120, 0) = 1
factorielle(6, 0xbf896570, 0xbf896578, 120, 0)   = 0
fprintf(0xb7896500, "%ld! = %lld\n", 6, ...6! = 720
)     = 9
+++ exited (status 0) +++
[factorielle]$</pre>
<p style="text-align: justify;">Tout d&#8217;abord il y a un mélange à l&#8217;écran entre la sortie standard et la sortie d&#8217;erreur, ce qui rend les comptes-rendus d&#8217;appel difficiles à lire. Envoyons la sortie d&#8217;erreur dans un fichier.</p>
<pre>[factorielle]$ <strong>ltrace ./test/calcule-factorielle 4 5 6 2&gt;traces.txt</strong>
4! = 24
5! = 120
6! = 720
[factorielle]$</pre>
<p style="text-align: justify;">Si notre processus utilise sa sortie d&#8217;erreur, on peut demander à <code>ltrace</code> d&#8217;envoyer ses messages directement dans un fichier à l&#8217;aide de son option <code>-o</code>.</p>
<pre>[factorielle]$ <strong>ltrace -o traces.txt ./test/calcule-factorielle 4 5 6 </strong>
4! = 24
5! = 120
6! = 720
[factorielle]$</pre>
<p style="text-align: justify;">Voyons ce que contient notre fichier de traces.</p>
<pre>[factorielle]$ <strong>cat traces.txt </strong>
__libc_start_main(0x80485b4, 4, 0xbf9384f4, 0x80486b0, 0x8048720 &lt;unfinished ...&gt;
_isoc99_sscanf(0xbf93a45f, 0x8048796, 0xbf938448, 0x80486d1, 0x8048500)                 = 1
factorielle(4, 0xbf938440, 0xbf938448, 0x80486d1, 0x8048500)                            = 0
fprintf(0xb771d500, "%ld! = %lld\n", 4, ...)                                            = 8
__isoc99_sscanf(0xbf93a461, 0x8048796, 0xbf938448, 24, 0)                               = 1
factorielle(5, 0xbf938440, 0xbf938448, 24, 0)                                           = 0
fprintf(0xb771d500, "%ld! = %lld\n", 5, ...)                                            = 9
__isoc99_sscanf(0xbf93a463, 0x8048796, 0xbf938448, 120, 0)                              = 1
factorielle(6, 0xbf938440, 0xbf938448, 120, 0)                                          = 0
fprintf(0xb771d500, "%ld! = %lld\n", 6, ...)                                            = 9
+++ exited (status 0) +++
[factorielle]$</pre>
<p style="text-align: justify;">Nous voyons bien nos appels de la fonction <code>factorielle()</code> mais ce qui est curieux, c&#8217;est que <code>ltrace</code> affiche cinq arguments pour notre routine, alors qu&#8217;elle n&#8217;en comporte que deux normalement (voir <a title="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-02-04/fact.h" target="_blank"><code>fact.h</code></a>). En fait, <code>ltrace</code> s&#8217;appuie sur un fichier de configuration nommé <code>/etc/ltrace.conf</code> qui contient le nombre et le type des arguments des fonctions des bibliothèques dynamiques du système. S&#8217;il ne trouve pas la fonction dans ce fichier, il affiche cinq arguments par défaut.</p>
<pre><strong>/etc/ltrace.conf</strong>
; ltrace.conf
;
; ~/.ltrace.conf will also be read, if it exists. The -F option may be
; used to suppress the automatic inclusion of both this file and
; ~/.ltrace.conf, and load a different config file or config files
; instead.
[...]
; arpa/inet.h
int inet_aton(string,addr);
string inet_ntoa(addr);                 ; It isn't an ADDR but an hexa number...
addr inet_addr(string);
[...]
; stdio.h
int fclose(file);
int feof(file);
int ferror(file);
int fflush(file);
char fgetc(file);
addr fgets(+string, int, file);
int fileno(file);
file fopen(string,string);
file fopen64(string,string);
int fprintf(file,format);
int fputc(char,file);
int fputs(string,file);
ulong fread(addr,ulong,ulong,file);
ulong fread_unlocked(addr,ulong,ulong,file);
ulong fwrite(string,ulong,ulong,file);
ulong fwrite_unlocked(string,ulong,ulong,file);
int pclose(addr);
void perror(string);
addr popen(string, string);
int printf(format);
int puts(string);
int remove(string);
int snprintf(+string2,ulong,format);
int sprintf(+string,format);
[...]
int   SYS_waitpid(int,addr,int);
ulong SYS_readv(int,addr,int);
ulong SYS_writev(int,addr,int);
int   SYS_mprotect(addr,int,int);
int   SYS_access(string,octal);</pre>
<p style="text-align: justify;">Bien entendu notre fonction ne figure pas dans ce fichier. Nous pourrions le modifier (si la bibliothèque était installée dans un emplacement du système accessible à tous les utilisateurs), mais je propose plutôt de créer un fichier supplémentaire <code>.ltrace.conf</code> que nous plaçons dans notre répertoire personnel (<code>ltrace</code> vient le chercher à cet emplacement).</p>
<pre>[factorielle]$ <strong>cat ~/<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/ltrace.conf" href="http://www.blaess.fr/christophe/files/article-2012-02-04/ltrace.conf" target="_blank">.ltrace.conf</a> </strong>
int factorielle(long,addr);
[factorielle]$ <strong>ltrace -o traces.txt ./test/calcule-factorielle 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorielle]$ <strong>cat traces.txt </strong>
__libc_start_main(0x80485b4, 4, 0xbf8b3764, 0x80486b0, 0x8048720
__isoc99_sscanf(0xbf8b545c, 0x8048796, 0xbf8b36b8, 0x80486d1, 0x8048500)           = 1
factorielle(4, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 4, ...)                                       = 8
__isoc99_sscanf(0xbf8b545e, 0x8048796, 0xbf8b36b8, 24, 0)                          = 1
factorielle(5, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 5, ...)                                       = 9
__isoc99_sscanf(0xbf8b5460, 0x8048796, 0xbf8b36b8, 120, 0)                         = 1
factorielle(6, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 6, ...)                                       = 9
+++ exited (status 0) +++
[factorielle]$</pre>
<p style="text-align: justify;">Nous pourrions aussi utiliser l&#8217;option <code>-F</code> de <code>ltrace</code> pour préciser les fichiers <code>ltrace.conf</code> à utiliser.</p>
<p style="text-align: justify;">Cette fois-ci le résultat est parfait, nous voyons bien les appels de notre fonction, avec la valeur à calculer, l&#8217;adresse du résultat à remplir et le statut de retour, <code>0</code> signifiant &laquo;&nbsp;tout va bien&nbsp;&raquo;.</p>
<p style="text-align: justify;">L&#8217;utilisation de <code>ltrace</code>, et de sa commande cousine <code>strace</code> pour les appels-système, est très utile pour la mise au point d&#8217;applications ou de bibliothèques. Son aspect non-intrusif (pas d&#8217;option de compilation particulière) et le fait que le code source ne soit pas nécessaire le rend utilisable même pour des programmes livrés sous forme binaire seulement. Je me souviens de l&#8217;avoir employé avec succès il y a quelques années sur une application à qui je devais fournir un fichier de configuration, mais pour lequel la documentation mentionnait un répertoire invalide. J&#8217;ai donc lancé <code>ltrace</code> sur l&#8217;application sans fournir de fichier de configuration. Bien entendu l&#8217;application a refusé de démarrer, mais j&#8217;ai pu chercher dans les traces (en filtrant avec <code>grep</code>) les lignes d&#8217;ouverture de fichiers &#8211; avec <code>fopen()</code> &#8211; et voir les tentatives successives avant échec (quelque chose comme <code>/home/cpb/.APPLICATION/</code>, <code>/usr/lib/APPLICATION</code>, <code>/etc/APPLICATION</code>…)</p>
<h1 style="text-align: justify;">Débogage avec Gdb</h1>
<p style="text-align: justify;">Dans la plupart des cas, on considère, lorsqu&#8217;on fait la mise au point d&#8217;une application, les appels de bibliothèques comme des invocations élémentaires, des fonctions sur le contenu desquelles on ne se pose pas de question. Pourtant, les bibliothèques dynamiques peuvent elles aussi nécessiter une mise au point et une analyse pas-à-pas de leur fonctionnement.</p>
<p style="text-align: justify;">Essayons d&#8217;utiliser le débogueur <code>gdb</code> sur l&#8217;exécutable que nous avons produit dans le précédent article.</p>
<pre>[factorielle]$ <strong>gdb ./test/calcule-factorielle </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorielle/test/calcule-factorielle...(no debugging symbols found)...done.
(gdb)</pre>
<p style="text-align: justify;">Le débogueur a bien trouvé notre exécutable et l&#8217;a chargé en mémoire. Toutefois, il nous indique qu&#8217;il ne dispose pas de la table de symboles nécessaires au débogage. Essayons quand même de poser un point d&#8217;arrêt en début de fonction <code>main</code>.</p>
<pre>(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485b9
(gdb)</pre>
<p style="text-align: justify;">Cela fonctionne. Lançons le programme avec une valeur en argument.</p>
<pre>(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorielle/test/calcule-factorielle 5
Breakpoint 1, 0x080485b9 in main ()
(gdb)</pre>
<p style="text-align: justify;">Nous sommes arrêtés en debut de <code>main()</code>, essayons d&#8217;avancer d&#8217;une instruction.</p>
<pre>(gdb) next
Single stepping until exit from function main,
which has no line number information.
5! = 120
0xb7e62113 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
(gdb)</pre>
<p style="text-align: justify;">Et oui, le débogueur n&#8217;a pas de notion de ligne de code, il va d&#8217;une seule traite jusqu&#8217;à la fin de <code>main()</code>. Nous ne pouvons que laisser le processus se terminer et quitter le débogueur.</p>
<pre>(gdb) <strong>cont</strong>
Continuing.
[Inferior 1 (process 15847) exited normally]
Undefined command: "exit".  Try "help".
(gdb) <strong>quit</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Compilons notre exécutable avec l&#8217;option <code>-g</code> pour y intégrer une table de correspondance entre les adresses mémoire et les lignes de code source. Puis réitérons l&#8217;expérience.</p>
<pre>[factorielle]$ <strong>gcc -I include/ -L lib/ -o test/calcule-factorielle test/calcule-factorielle.c -l fact -g</strong>
[factorielle]$ <strong>gdb ./test/calcule-factorielle </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;
Reading symbols from /home/cpb/factorielle/test/calcule-factorielle...done.
(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485bf: file test/calcule-factorielle.c, line 11.
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorielle/test/calcule-factorielle 5

Breakpoint 1, main (argc=2, argv=0xbffff274) at test/calcule-factorielle.c:11
11		if (argc &lt; 2) {
(gdb)</pre>
<p style="text-align: justify;">Cette fois nous remarquons que <code>gdb</code> nous affiche correctement la ligne 11. Continuons à avancer en pas-à-pas avec la commande <code>step</code>.</p>
<pre>(gdb) <strong>step</strong>
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
16			if (sscanf(argv[i], "%ld", &amp; n) == 1) {
(gdb) <strong>step</strong>
17				if (factorielle(n, &amp; f) == 0)
(gdb)</pre>
<p style="text-align: justify;">Malheureusement <code>gdb</code> ne dispose pas des sources de la bibliothèque, aussi ne nous permet-il pas de rentrer dans la fonction <code>factorielle()</code>, pas plus qu&#8217;il ne le fait pour <code>sscanf()</code> ou <code>fprintf()</code>.</p>
<pre>(gdb) <strong>step</strong>
18					fprintf(stdout, "%ld! = %lld\n", n, f);
(gdb) <strong>step</strong>
5! = 120
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
22		return EXIT_SUCCESS;
(gdb) <strong>step</strong>
23	}
(gdb) <strong>step</strong>
0xb7e62113 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
(gdb) <strong>step</strong>
Single stepping until exit from function __libc_start_main,
which has no line number information.
[Inferior 1 (process 27455) exited normally]
(gdb) <strong>quit</strong>
[factorielle]$</pre>
<h2>Débogage de la bibliothèque</h2>
<p style="text-align: justify;">Nous devons compiler notre bibliothèque avec l&#8217;option <code>-g</code> ainsi.</p>
<pre>[factorielle]$ <strong>gcc -I include/ -o src/fact.o -c src/fact.c -g</strong>
[factorielle]$ <strong>gcc -shared -I include/ -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 src/fact.o </strong>
[factorielle]$ <strong>gcc -I include/ -L lib/ -o test/calcule-factorielle test/calcule-factorielle.c -l fact -g</strong></pre>
<p style="text-align: justify;">Puis lancer le débogage.</p>
<pre>[factorielle]$ <strong>gdb ./test/calcule-factorielle </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorielle/test/calcule-factorielle...done.
(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485bf: file test/calcule-factorielle.c, line 11.
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorielle/test/calcule-factorielle 5
(gdb) <strong>break main</strong>
Breakpoint 1, main (argc=2, argv=0xbffff274) at test/calcule-factorielle.c:11
11		if (argc &lt; 2) {
(gdb) <strong>step</strong>
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
16			if (sscanf(argv[i], "%ld", &amp; n) == 1) {
(gdb) <strong>step</strong>
17				if (factorielle(n, &amp; f) == 0)
(gdb) <strong>step</strong>
factorielle (n=5, result=0xbffff1c0) at src/fact.c:5
5		* result = 1;
(gdb)</pre>
<p style="text-align: justify;">Nous sommes bien dans notre routine <code>factorielle()</code>, continuons quelques instructions en pas-à-pas&#8230;</p>
<pre>(gdb) <strong>step</strong>
6		if (n &lt; 0)
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>step</strong>
10			n = n - 1;
(gdb) <strong>step</strong>
11		} while (n &gt; 1);
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>step</strong>
10			n = n - 1;
(gdb)</pre>
<p style="text-align: justify;">Nous pouvons également examiner l&#8217;état des variables. La présentation du résultat par <code>gdb</code> est un peu surprenante de prime abord, car il préfixe les expressions d&#8217;un <code>$</code> suivi d&#8217;un numéro d&#8217;ordre de ses évaluations. Ceci permet d&#8217;écrire facilement des frontaux graphiques (comme <code>ddd</code>, <code>xxgdb</code>, Eclipse, etc.) qui récupèrent les valeurs renvoyées.</p>
<pre>(gdb) <strong>print *result</strong>
$1 = 20
(gdb) <strong>print n</strong>
$2 = 4
(gdb) <strong>cont</strong>
Continuing.
5! = 120
[Inferior 1 (process 30206) exited normally]
(gdb) <strong>quit</strong>
[factorielle]$</pre>
<h2>Emplacement des sources</h2>
<p style="text-align: justify;">L&#8217;emplacement des sources de la bibliothèque est mentionné dans le fichier exécutable lors de la compilation avec l&#8217;option <code>-g</code>. Toutefois elles peuvent être déplacées ou la bibliothèque peut être compilée sur une autre machine que celle utilisée pour le débogage. Il faut donc disposer d&#8217;un moyen d&#8217;indiquer à <code>gdb</code> l&#8217;endroit où se trouvent les fichiers source de la bibliothèque. Faisons un essai en déplaçant les sources de la bibliothèque dans un répertoire totalement indépendant.</p>
<pre>[factorielle]$ <strong>mkdir -p ~/tmp/sources</strong>
[factorielle]$ <strong>mv src/* ~/tmp/sources/</strong>
[factorielle]$ <strong>gdb ./test/calcule-factorielle </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorielle/test/calcule-factorielle...done.
(gdb) <strong>break factorielle</strong>
Breakpoint 1 at 0x80484f0
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorielle/test/calcule-factorielle 5

Breakpoint 1, factorielle (n=5, result=0xbffff1c0) at src/fact.c:5
5	src/fact.c: Aucun fichier ou dossier de ce type.
	in src/fact.c
(gdb) <strong>quit</strong>
A debugging session is active.

	Inferior 1 [process 31536] will be killed.

Quit anyway? (y or n) <strong>y</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Évidemment le débogage échoue. Utilisons à présent la commande <code>directory</code> de <code>gdb</code>.</p>
<pre>[factorielle]$ <strong>gdb ./test/calcule-factorielle </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorielle/test/calcule-factorielle...done.
(gdb)<strong> directory ~/tmp/sources/</strong>
Source directories searched: /home/cpb/tmp/sources:$cdir:$cwd
(gdb) <strong>break factorielle</strong>
Breakpoint 1 at 0x80484f0
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorielle/test/calcule-factorielle 5

Breakpoint 1, factorielle (n=5, result=0xbffff1c0) at src/fact.c:5
5		* result = 1;
(gdb) <strong>step</strong>
6		if (n &lt; 0)
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>cont</strong>
Continuing.
5! = 120
[Inferior 1 (process 31845) exited normally]
(gdb) <strong>quit</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Cela fonctionne parfaitement.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">Nous avons examiné deux étapes importantes pour la mise au point d&#8217;une bibliothèque : le suivi des appels et le débogage pas-à-pas. Nous examinerons les tests en couverture dans le prochain article.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Development of a dynamic library (2/3)</title>
		<link>http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/</link>
		<comments>http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/#comments</comments>
		<pubDate>Sat, 04 Feb 2012 08:59:26 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1572</guid>
		<description><![CDATA[(Version originale en français) In the previous article we saw how to build a dynamic library and properly manage its major and minor version numbers for easy maintenance, both for the developer (of the library but also of the applications that use it) and for the administrator of the system where it is installed. We [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(<a title="Mise au point de bibliothèque dynamique (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/">Version originale en français</a>)</p>
<p style="text-align: justify;">In <a title="Development of a dynamic library (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/" target="_blank">the previous article</a> we saw how to build a dynamic library and properly manage its major and minor version numbers for easy maintenance, both for the developer (of the library but also of the applications that use it) and for the administrator of the system where it is installed. We will now consider how to debug our library and applications that call it.</p>
<h1>Calls tracking</h1>
<p style="text-align: justify;">The first tool to know is <code>ltrace</code>. It sends to the error output of the process a log of all function calls to dynamic libraries. We put ourselves back in the same situation as for the previous article with the following directories:</p>
<ul>
<li style="text-align: justify;"><code>factorial/src</code> contains the source code of the library</li>
<li style="text-align: justify;"><code>factorial/include</code> where the header files of the library are</li>
<li style="text-align: justify;"><code>factorial/lib</code> containing the compiled files of the library</li>
<li style="text-align: justify;"><code>factorial/test</code> where we find source codes and executable files of the applications using the library</li>
</ul>
<p style="text-align: justify;">Here is an overview of the content of our directory:</p>
<pre>[~] <strong>cd factorial</strong>
[factorial]$ <strong>ls</strong>
include  lib  src  test
[factorial]$ <strong>ls include/</strong>
<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.h" target="_blank">fact.h</a>
[factorial]$ <strong>ls -l lib/</strong>
total 8
lrwxrwxrwx 1 cpb cpb   12 2012-02-04 05:04 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb   14 2012-02-04 05:04 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-02-04 05:04 libfact.so.2.0
[factorial]$ <strong>ls src/</strong>
<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.c" href="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.c" target="_blank">fact.c</a>  fact.o
[factorial]$ <strong>ls test/</strong>
factorial  <a title="http://www.blaess.fr/christophe/files/article-2012-02-04/en/factorial.c" href="http://www.blaess.fr/christophe/files/article-2012-02-04/en/factorial.c" target="_blank">factorial.c</a>
[factorial]$</pre>
<p style="text-align: justify;">Details to create executable files and symbolic links were in <a title="Development of a dynamic library (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/" target="_blank">the previous article</a>. Here is an example of the test program.</p>
<pre>[factorial]$ <strong>export LD_LIBRARY_PATH=lib/</strong>
[factorial]$ <strong>./test/factorial 7</strong>
7! = 5040
[factorial]$</pre>
<p style="text-align: justify;">Let&#8217;s use <code>ltrace</code> to see library function calls.</p>
<pre>[factorial]$ <strong>ltrace ./test/factorial 4 5 6</strong>
__libc_start_main(0x80485b4, 4, 0xbf896624, 0x80486b0, 0x8048720
__isoc99_sscanf(0xbf89745f, 0x8048796, 0xbf896578, 0x80486d1, 0x8048500) = 1
factorial(4, 0xbf896570, 0xbf896578, 0x80486d1, 0x8048500) = 0
fprintf(0xb7896500, "%ld! = %lld\n", 4, ...4! = 24
)     = 8
__isoc99_sscanf(0xbf897461, 0x8048796, 0xbf896578, 24, 0) = 1
factorial(5, 0xbf896570, 0xbf896578, 24, 0)    = 0
fprintf(0xb7896500, "%ld! = %lld\n", 5, ...5! = 120
)     = 9
__isoc99_sscanf(0xbf897463, 0x8048796, 0xbf896578, 120, 0) = 1
factorial(6, 0xbf896570, 0xbf896578, 120, 0)   = 0
fprintf(0xb7896500, "%ld! = %lld\n", 6, ...6! = 720
)     = 9
+++ exited (status 0) +++
[factorial]$</pre>
<p style="text-align: justify;">First of all there is a mix between the standard output display and the error output content, making the reports difficult to read. We will send error output to a file:</p>
<pre>[factorial]$ <strong>ltrace ./test/factorial 4 5 6 2&gt;traces.txt</strong>
4! = 24
5! = 120
6! = 720
[factorial]$</pre>
<p style="text-align: justify;">If our process uses its error output, we can ask <code>ltrace</code> to put its messages directly into a file, using the <code>-o</code> option.</p>
<pre>[factorial]$ <strong>ltrace -o traces.txt ./test/factorial 4 5 6 </strong>
4! = 24
5! = 120
6! = 720
[factorial]$</pre>
<p style="text-align: justify;">Let&#8217;s see the content of our trace file.</p>
<pre>[factorial]$ <strong>cat traces.txt </strong>
__libc_start_main(0x80485b4, 4, 0xbf9384f4, 0x80486b0, 0x8048720 &lt;unfinished ...&gt;
_isoc99_sscanf(0xbf93a45f, 0x8048796, 0xbf938448, 0x80486d1, 0x8048500)                 = 1
factorial(4, 0xbf938440, 0xbf938448, 0x80486d1, 0x8048500)                            = 0
fprintf(0xb771d500, "%ld! = %lld\n", 4, ...)                                            = 8
__isoc99_sscanf(0xbf93a461, 0x8048796, 0xbf938448, 24, 0)                               = 1
factorial(5, 0xbf938440, 0xbf938448, 24, 0)                                           = 0
fprintf(0xb771d500, "%ld! = %lld\n", 5, ...)                                            = 9
__isoc99_sscanf(0xbf93a463, 0x8048796, 0xbf938448, 120, 0)                              = 1
factorial(6, 0xbf938440, 0xbf938448, 120, 0)                                          = 0
fprintf(0xb771d500, "%ld! = %lld\n", 6, ...)                                            = 9
+++ exited (status 0) +++
[factorial]$</pre>
<p style="text-align: justify;">Indeed we see the call to our <code>factorial()</code> function, but there is something strange: <code>ltrace</code> prints five parameters for our function, but there are in fact only two parameters (see <a title="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-02-04/en/fact.h" target="_blank"><code>fact.h</code></a>).<br />
In fact, <code>ltrace</code> relies on a configuration file named <code>/etc/ltrace.conf</code> that contains the number and type of arguments of the functions of the system dynamic libraries. If it does not find the function in this file, it displays five arguments by default.</p>
<pre><strong>/etc/ltrace.conf</strong>
; ltrace.conf
;
; ~/.ltrace.conf will also be read, if it exists. The -F option may be
; used to suppress the automatic inclusion of both this file and
; ~/.ltrace.conf, and load a different config file or config files
; instead.
[...]
; arpa/inet.h
int inet_aton(string,addr);
string inet_ntoa(addr);                 ; It isn't an ADDR but an hexa number...
addr inet_addr(string);
[...]
; stdio.h
int fclose(file);
int feof(file);
int ferror(file);
int fflush(file);
char fgetc(file);
addr fgets(+string, int, file);
int fileno(file);
file fopen(string,string);
file fopen64(string,string);
int fprintf(file,format);
int fputc(char,file);
int fputs(string,file);
ulong fread(addr,ulong,ulong,file);
ulong fread_unlocked(addr,ulong,ulong,file);
ulong fwrite(string,ulong,ulong,file);
ulong fwrite_unlocked(string,ulong,ulong,file);
int pclose(addr);
void perror(string);
addr popen(string, string);
int printf(format);
int puts(string);
int remove(string);
int snprintf(+string2,ulong,format);
int sprintf(+string,format);
[...]
int   SYS_waitpid(int,addr,int);
ulong SYS_readv(int,addr,int);
ulong SYS_writev(int,addr,int);
int   SYS_mprotect(addr,int,int);
int   SYS_access(string,octal);</pre>
<p style="text-align: justify;">Of course our function does not appear in this file. We could change it (if the library was installed in a location accessible to all users), but I propose instead to create an additional file <code>.ltrace.conf</code> that we put in our home directory (where <code>ltrace</code> look for additional configuration files).</p>
<pre>[factorial]$ <strong>cat ~/<a title="http://www.blaess.fr/christophe/files/article-2012-02-04/en/ltrace.conf" href="http://www.blaess.fr/christophe/files/article-2012-02-04/en/ltrace.conf" target="_blank">.ltrace.conf</a> </strong>
int factorial(long,addr);
[factorial]$ <strong>ltrace -o traces.txt ./test/factorial 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorial]$ <strong>cat traces.txt </strong>
__libc_start_main(0x80485b4, 4, 0xbf8b3764, 0x80486b0, 0x8048720
__isoc99_sscanf(0xbf8b545c, 0x8048796, 0xbf8b36b8, 0x80486d1, 0x8048500)           = 1
factorial(4, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 4, ...)                                       = 8
__isoc99_sscanf(0xbf8b545e, 0x8048796, 0xbf8b36b8, 24, 0)                          = 1
factorial(5, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 5, ...)                                       = 9
__isoc99_sscanf(0xbf8b5460, 0x8048796, 0xbf8b36b8, 120, 0)                         = 1
factorial(6, 0xbf8b36b0)                                                         = 0
fprintf(0xb7704500, "%ld! = %lld\n", 6, ...)                                       = 9
+++ exited (status 0) +++
[factorial]$</pre>
<p style="text-align: justify;">We may also use the <code>-F</code> option to specify which <code>ltrace.conf</code> file to use.</p>
<p style="text-align: justify;">This time the result is perfect, we see our function calls, with the value to compute, the address of the result to complete and the return status (<code>0</code> means &laquo;&nbsp;all right&nbsp;&raquo;).</p>
<p style="text-align: justify;">Using <code>ltrace</code>, and its cousin command <code>strace</code> for system calls, is very usefull when developping applications and library. Non-intrusive (no special compiler option) and no requirement for source code make it usable even on programs deliveres in binary form only. I remember having used it successfully a few years ago on an application that needed a configuration file, but the documentation referred to an invalid directory. So I started <code>ltrace</code> on the application without providing the configuration file. Of course the application failed to start, but I could find in the logs (filtered with <code>grep</code>) the calls to the <code>fopen()</code> library function, and see the sucessive attempts before failure (something like <code>~/.APPLICATION/</code>, <code>/usr/lib/APPLICATION</code>, <code>/etc/APPLICATION</code> …)</p>
<h1 style="text-align: justify;">Debugging with Gdb</h1>
<p style="text-align: justify;">In most cases during application development, we consider library calls as invocations of elementary functions on whose contents we do not question. However, dynamic libraries may also require development and step-by-step analysis.</p>
<p style="text-align: justify;">So, let&#8217;s try using <code>gdb</code> with the executable file we produced in the previons article.</p>
<pre>[factorial]$ <strong>gdb ./test/factorial </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorial/test/factorial...(no debugging symbols found)...done.
(gdb)</pre>
<p style="text-align: justify;">The debugger has found our executable and loaded it into memory. However, he can&#8217;t find any symbol table needed for debugging. Let&#8217;s just try to put a breakpoint at the start of <code>main</code> function.</p>
<pre>(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485b9
(gdb)</pre>
<p style="text-align: justify;">It works. Run the program with a value argument.</p>
<pre>(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorial/test/factorial 5
Breakpoint 1, 0x080485b9 in main ()
(gdb)</pre>
<p style="text-align: justify;">We stopped in the beginning of <code>main()</code>. Try to move one step forward.</p>
<pre>(gdb) next
Single stepping until exit from function main,
which has no line number information.
5! = 120
0xb7e62113 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
(gdb)</pre>
<p style="text-align: justify;">Indeed, the debugger has no notion of line of code, it goes in one run until the end of <code>main()</code>. We can only allow the process to complete and exit the debugger.</p>
<pre>(gdb) <strong>cont</strong>
Continuing.
[Inferior 1 (process 15847) exited normally]
Undefined command: "exit".  Try "help".
(gdb) <strong>quit</strong>
[factorial]$</pre>
<p style="text-align: justify;">Now, we compile our code with the <code>-g</code> option, in order to insert in the executable file a table of associations between memory addresses and lines of code. Then repeat the experience.</p>
<pre>[factorial]$ <strong>gcc -I include/ -L lib/ -o test/factorial test/factorial.c -l fact -g</strong>
[factorial]$ <strong>gdb ./test/factorial </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;
Reading symbols from /home/cpb/factorial/test/factorial...done.
(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485bf: file test/factorial.c, line 11.
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorial/test/factorial 5

Breakpoint 1, main (argc=2, argv=0xbffff274) at test/factorial.c:11
11		if (argc &lt; 2) {
(gdb)</pre>
<p style="text-align: justify;">This time, we note that <code>gdb</code> displays correctly the line 11. Continue to move forward instruction-by-instruction with the <code>step</code> command.</p>
<pre>(gdb) <strong>step</strong>
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
16			if (sscanf(argv[i], "%ld", &amp; n) == 1) {
(gdb) <strong>step</strong>
17				if (factorial(n, &amp; f) == 0)
(gdb) <strong>step</strong>
18                                  fprintf(stdout, "%ld! = %lld\n", n, f);
(gdb)</pre>
<p style="text-align: justify;">Unfortunately <code>gdb</code> does not have the sources of the library, so we can not enter the <code>factorial()</code> function, no more than we could in <code>sscanf()</code> or <code>fprintf()</code>.</p>
<pre>(gdb) <strong>step</strong>
5! = 120
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
22		return EXIT_SUCCESS;
(gdb) <strong>step</strong>
23	}
(gdb) <strong>step</strong>
0xb7e62113 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
(gdb) <strong>step</strong>
Single stepping until exit from function __libc_start_main,
which has no line number information.
[Inferior 1 (process 27455) exited normally]
(gdb) <strong>quit</strong>
[factorial]$</pre>
<h2>Debugging the library</h2>
<p style="text-align: justify;">We have to compile the library with the <code>-g</code> option.</p>
<pre>[factorial]$ <strong>gcc -I include/ -o src/fact.o -c src/fact.c -g</strong>
[factorial]$ <strong>gcc -shared -I include/ -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 src/fact.o </strong>
[factorial]$ <strong>gcc -I include/ -L lib/ -o test/factorial test/factorial.c -l fact -g</strong></pre>
<p style="text-align: justify;">And start debugging…</p>
<pre>[factorial]$ <strong>gdb ./test/factorial </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorial/test/factorial...done.
(gdb) <strong>break main</strong>
Breakpoint 1 at 0x80485bf: file test/factorial.c, line 11.
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorial/test/factorial 5
(gdb) <strong>break main</strong>
Breakpoint 1, main (argc=2, argv=0xbffff274) at test/factorial.c:11
11		if (argc &lt; 2) {
(gdb) <strong>step</strong>
15		for (i = 1; i &lt; argc; i ++)
(gdb) <strong>step</strong>
16			if (sscanf(argv[i], "%ld", &amp; n) == 1) {
(gdb) <strong>step</strong>
17				if (factorial(n, &amp; f) == 0)
(gdb) <strong>step</strong>
factorial (n=5, result=0xbffff1c0) at src/fact.c:5
5		* result = 1;
(gdb)</pre>
<p style="text-align: justify;">Now we really are into our <code>factorial()</code> function,  let&#8217;s continue step-by-step a few instructions more…</p>
<pre>(gdb) <strong>step</strong>
6		if (n &lt; 0)
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>step</strong>
10			n = n - 1;
(gdb) <strong>step</strong>
11		} while (n &gt; 1);
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>step</strong>
10			n = n - 1;
(gdb)</pre>
<p style="text-align: justify;">We can also look at the state of the variables. The <code>gdb</code> printing of the results is a bit surprising at first because it prefixes the evaluated terms by a <code>$</code> character followed by a serial number. This allows to easily write graphical front-ends (like <code>ddd</code>, <code>xxgdb</code>, Eclipse, etc..) that retrieve the returned values.</p>
<pre>(gdb) <strong>print *result</strong>
$1 = 20
(gdb) <strong>print n</strong>
$2 = 4
(gdb) <strong>cont</strong>
Continuing.
5! = 120
[Inferior 1 (process 30206) exited normally]
(gdb) <strong>quit</strong>
[factorial]$</pre>
<h2>Source files locatoin</h2>
<p style="text-align: justify;">The source files location is stored into the executable file when compiling with the <code>-g</code> option. However they can be moved or library can be compiled on another machine that those used for debugging. We must therefore have a way to tell <code>gdb</code> where to find the source files of the library. Do a test by moving the sources from the library into a totally different directory.</p>
<pre>[factorial]$ <strong>mkdir -p ~/tmp/sources</strong>
[factorial]$ <strong>mv src/* ~/tmp/sources/</strong>
[factorial]$ <strong>gdb ./test/factorial </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorial/test/factorial...done.
(gdb) <strong>break factorial</strong>
Breakpoint 1 at 0x80484f0
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorial/test/factorial 5

Breakpoint 1, factorial (n=5, result=0xbffff1c0) at src/fact.c:5
5	src/fact.c: No such file or directory.
	in src/fact.c
(gdb) <strong>quit</strong>
A debugging session is active.

	Inferior 1 [process 31536] will be killed.

Quit anyway? (y or n) <strong>y</strong>
[factorial]$</pre>
<p style="text-align: justify;">Of course the debugging failed. We will now try the <code>directory</code> command at the <code>gdb</code> prompt.</p>
<pre>[factorial]$ <strong>gdb ./test/factorial </strong>
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
&lt;http://bugs.launchpad.net/gdb-linaro/&gt;...
Reading symbols from /home/cpb/factorial/test/factorial...done.
(gdb)<strong> directory ~/tmp/sources/</strong>
Source directories searched: /home/cpb/tmp/sources:$cdir:$cwd
(gdb) <strong>break factorial</strong>
Breakpoint 1 at 0x80484f0
(gdb) <strong>run 5</strong>
Starting program: /home/cpb/factorial/test/factorial 5

Breakpoint 1, factorial (n=5, result=0xbffff1c0) at src/fact.c:5
5		* result = 1;
(gdb) <strong>step</strong>
6		if (n &lt; 0)
(gdb) <strong>step</strong>
9			(*result) = (*result) * n;
(gdb) <strong>cont</strong>
Continuing.
5! = 120
[Inferior 1 (process 31845) exited normally]
(gdb) <strong>quit</strong>
[factorial]$</pre>
<p style="text-align: justify;">It works perfectly.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">We have seen two important steps in the development of a library: function calls tracking and step-by-step debugging. We will examine the coverage tests in the <a title="Development of a dynamic library (3/3)" href="http://www.blaess.fr/christophe/2012/02/12/development-of-a-dynamic-library-33/">next article</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mise au point de bibliothèque dynamique (1/3)</title>
		<link>http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/</link>
		<comments>http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/#comments</comments>
		<pubDate>Sat, 28 Jan 2012 09:00:43 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1454</guid>
		<description><![CDATA[(English translation here) Lors d&#8217;une récente session de formation, une conversation avec un participant m&#8217;a poussé à vérifier les options nécessaires pour effectuer du débogage et des tests en couverture sur une bibliothèque partagée. Une bibliothèque dynamique (fichier libXXXX.so &#8211; so pour Shared Object) est chargée dans la mémoire du processus au moment de son [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(<a title="Development of a dynamic library (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/">English translation here</a>)</p>
<p style="text-align: justify;">Lors d&#8217;une récente session de formation, une conversation avec un participant m&#8217;a poussé à vérifier les options nécessaires pour effectuer du débogage et des tests en couverture sur une bibliothèque partagée.</p>
<p style="padding-left: 60px; text-align: justify;">Une bibliothèque dynamique (fichier <code>lib<em>XXXX</em>.so</code> &#8211; <code>so</code> pour <em>Shared Object</em>) est chargée dans la mémoire du processus au moment de son démarrage. Le fichier exécutable et la bibliothèque sont indépendants avant le lancement de l&#8217;application, et peuvent être maintenus séparément.</p>
<p style="text-align: justify;">J&#8217;ai réalisé que certains points étaient loin d&#8217;être évidents, par exemple la gestion des numéros de version ou l&#8217;activation des tests en couverture. Voici un petit récapitulatif des étapes de mise au point d&#8217;une bibliothèque dynamique. Le premier article est consacré à la compilation, la gestion des versions et la création des liens symboliques nécessaires. <a title="Mise au point de bibliothèque dynamique (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/" target="_blank">Le second</a> s&#8217;intéressera au débogage et au suivi pas-à-pas du code de la bibliothèque depuis une application. Le troisième décrira comment effectuer des tests en couverture sur le contenu de la bibliothèque.</p>
<h1>Compilation et installation de la bibliothèque</h1>
<h2>Compilation du code de la bibliothèque</h2>
<p style="text-align: justify;">Commençons par créer une petite bibliothèque dynamique, avec une fonction relativement simple : l&#8217;implémentation de la fonction mathématique &laquo;&nbsp;factorielle&nbsp;&raquo;.</p>
<p style="text-align: justify;">Je crée un répertoire de travail <code>factorielle</code> regroupant tous les fichiers concernant cette bibliothèque. Nous y créons trois sous-répertoires :</p>
<ul>
<li style="text-align: justify;"><code>src/</code> qui contiendra le code source de la bibliothèque,</li>
<li style="text-align: justify;"><code>lib/</code> où seront regoupés les fichiers binaires et les liens symboliques décrits plus bas,</li>
<li style="text-align: justify;"><code>include/</code> dans lequel les fichiers d&#8217;en-tête de la bibliothèque seront stockés.</li>
</ul>
<pre>[~]$ <strong>mkdir factorielle</strong>
[~]$ <strong>mkdir factorielle/src</strong>
[~]$ <strong>mkdir factorielle/include</strong>
[~]$ <strong>mkdir factorielle/lib</strong>
[~]$ <strong>cd factorielle</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Créons un fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/fact.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/fact.c" target="_blank"><code>src/fact.c</code></a> implémentant notre fonction.</p>
<p style="text-align: justify; padding-left: 60px;">Et si vous pensez avoir trouvé un bug dans le code ci-dessous, ayez la gentillesse de lire l&#8217;article en entier avant de m&#8217;envoyer un mail de moquerie <img src='http://www.blaess.fr/christophe/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<pre>#include &lt;fact.h&gt;

long long int factorielle(long int n)
{
        long long int f = 1;
        do {
                f = f * n;
                n = n - 1;
        } while (n &gt; 1);
        return f;
}</pre>
<p style="text-align: justify;">Ce fichier commence par inclure son propre fichier d&#8217;en-tête, ce qui permet de s&#8217;assurer à la compilation de la concordance du prototype et de l&#8217;implémentation.</p>
<p style="text-align: justify;">Le fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-01-28/fact.h" target="_blank"><code>include/fact.h</code></a> contient les lignes suivantes.</p>
<pre>#ifndef LIB_FACT_H
#define LIB_FACT_H
    long long int factorielle(long int n);
#endif</pre>
<p style="text-align: justify;">Lors de la compilation de ce fichier nous fournirons sur la ligne de commande de <code>gcc</code> les options:</p>
<ul>
<li style="text-align: justify;"><code>-c</code> pour arrêter <code>gcc</code> après la phase de compilation et obtenir ainsi un fichier objet (pas d&#8217;édition des liens).</li>
<li style="text-align: justify;"><code>-I include/</code> qui indique à <code>gcc</code> de rechercher les fichiers d&#8217;en-tête <code>.h</code> dans le répertoire <code>include/</code> en supplément des répertoires usuels (<code>/usr/include</code>…).</li>
<li style="text-align: justify;"><code>-fPIC</code> pour demander la génération d&#8217;un code relogeable (<em>Position Independant Code</em>) comme c&#8217;est nécessaire pour la création de bibliothèques partagées même si cette option n&#8217;est pas  indispensable sur certaines architectures (x86 32 bits par exemple).</li>
</ul>
<p style="text-align: justify;">Voici un exemple de compilation.</p>
<pre>[factorielle]$ <strong>ls src/</strong>
fact.c
[factorielle]$ <strong>ls include/</strong>
fact.h
[factorielle]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact.c</strong>
[factorielle]$ <strong>ls src/</strong>
fact.c fact.o
[factorielle]$</pre>
<p style="text-align: justify;">Cette compilation a généré un fichier objet <code>fact.o</code> que nous utiliserons ci-après.</p>
<p style="text-align: justify;">On notera que durant la phase de test et de mise au point, il ne faut utiliser aucune option d&#8217;optimisation, sinon le compilateur risque de modifier le code exécutable créé (en regroupant des blocs de code par exemple) et il n&#8217;y aura plus de correspondance exacte avec le fichier source.</p>
<h2>Génération de la bibliothèque</h2>
<p style="text-align: justify;">La bibliothèque proprement dite est obtenue en invoquant <code>gcc</code> avec l&#8217;option <code>-shared</code>. Nous allons lui demander d&#8217;enregistrer la bibliothèque dans le fichier <code>libfact.so.1.0</code>. Les numéros <code>1</code> et <code>0</code> correspondent respectivement aux numéros majeur et mineur de version de la bibliothèque.</p>
<p style="text-align: justify;">Il est d&#8217;usage de considérer qu&#8217;un changement de numéro majeur représente une rupture de la compatibilité binaire de la bibliothèque et nécessite une recompilation des applications, alors qu&#8217;une variation du numéro mineur signifie des corrections ou des améliorations internes n&#8217;influant pas sur l&#8217;interface de programmation.</p>
<p style="text-align: justify;">Nous allons indiquer à <code>gcc</code> d&#8217;enregistrer dans l&#8217;en-tête de la bibliothèque son nom officiel incluant le numéro majeur de version avec l&#8217;option <code>-Wl</code>. Celle-ci transmet au <em>linker</em> la chaîne de caractères qui la suit après avoir remplacé les virgules par des espaces. C&#8217;est donc l&#8217;option <code>-soname libfact.so.1</code> qui est passée.</p>
<p style="text-align: justify;">Il est conseillé de répêter les options passées lors de la compilation précédente, comme <code>-fPIC</code>.</p>
<pre>[factorielle]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.1 -o lib/libfact.so.1.0 src/fact.o</strong>
[factorielle]$ <strong>ls lib/</strong>
libfact.so.1.0
[factorielle]$</pre>
<p style="text-align: justify;">Nous disposons donc d&#8217;un fichier <code>libfact.so.1.0</code> dont l&#8217;en-tête contient le nom <code>libfact.so.1</code></p>
<h2>Création des liens symboliques</h2>
<p style="text-align: justify;">Lorsque nous compilerons une application, nous préciserons à <code>gcc</code> de la lier avec la bibliothèque <code>fact</code>. Celui-ci recherchera un fichier <code>libfact.so</code>. Et non pas <code>libfact.so.1.0</code>. Aussi va-t-il falloir créer un lien symbolique pour indiquer le chemin vers le fichier. Ce lien est créé manuellement avec la commande <code>ln</code>.</p>
<pre>[factorielle]$ <strong>cd lib/</strong>
[lib]$ <strong>ln -sf libfact.so.1.0 libfact.so</strong>
[lib]$ <strong>ls -l lib*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:04 libfact.so -&gt; libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
[lib]$</pre>
<p style="text-align: justify;">Lors de la compilation, <code>gcc</code> enregistrera dans le fichier exécutable généré le nom de la bibliothèque qu&#8217;il a utilisé. Il s&#8217;agit du nom &laquo;&nbsp;officiel&nbsp;&raquo; qu&#8217;il trouve dans la section <code>SONAME</code> que nous avons renseignée avec l&#8217;argument <code>-Wl,-soname</code> précédement.</p>
<p style="text-align: justify;">A l&#8217;exécution, le chargeur recherche donc la bibliothèque dont le numéro majeur correspond à celui utilisé lors de la compilation. Il va donc falloir qu&#8217;il trouve un fichier <code>libfact.so.1</code>, ou plutôt un lien <code>libfact.so.1</code> qui pointe vers <code>libfact.so.1.0</code>.</p>
<p style="text-align: justify;">La création du premier lien symbolique était nécessaire pour pouvoir <em>compiler</em> une application avec la bibliothèque, le second lien est indispensable pour pouvoir <em>exécuter</em> un programme lié avec elle. Ce lien est donc utilisé beaucoup plus fréquemment que le précédent. Pour simplifier la vie de l&#8217;administrateur, une commande nommée <code>ldconfig</code> va l&#8217;aider à créer automatiquement les liens dont son système a besoin pour que les utilisateurs puissent exécuter les applications. Elle parcourt les répertoires-système contenant des bibliothèques (<code>/lib</code>, <code>/usr/lib</code>, <code>/usr/local/lib</code>, etc. plus tous ceux indiqués dans <code>/etc/ld.so.conf</code>) et crée sur chaque fichier de bibliothèque un lien avec le nom contenu dans sa section <code>SONAME</code>. Nous allons en voir un exemple en forçant <code>ldconfig</code> à explorer uniquement notre répertoire grâce à son option <code>-n</code>.</p>
<pre>[lib]$ <strong>ldconfig -n .</strong>
[lib]$ <strong>ls -l lib*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:04 libfact.so -&gt; libfact.so.1.0
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:05 libfact.so.1 -&gt; libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
[lib]$</pre>
<p style="text-align: justify;">Les liens présents permettront donc de compiler une application nécessitant notre bibliothèque (par le biais de <code>libfact.so</code>) puis de l&#8217;exécuter en s&#8217;assurant que la version majeure soit la bonne (grâce à <code>libfact.so.1</code>).</p>
<h1>Utilisation de la bibliothèque</h1>
<h2>Compilation d&#8217;une application</h2>
<p style="text-align: justify;">Écrivons un petit programme qui utilise notre bibliothèque. Le fichier <code><a title="http://www.blaess.fr/christophe/files/article-2012-01-28/factorielle.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/factorielle.c" target="_blank">factorielle.c</a></code> va invoquer notre fonction <code>factorielle()</code> sur tous les nombres fournis sur sa ligne de commande.</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fact.h&gt;

int main (int argc, char * argv[])
{
        long int n;
        int i;
        if (argc &lt; 2) {
                fprintf(stderr, "usage: %s valeurs...\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        for (i = 1; i &lt; argc; i ++)
                if (sscanf(argv[i], "%ld", &amp; n) == 1)
                        fprintf(stdout, "%ld! = %lld\n", n, factorielle(n));
        return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Ce programme se trouve dans le répertoire <code>factorielle/test/</code> que nous créons pour l&#8217;occasion. Il inclut le fichier d&#8217;en-tête <code>&lt;fact.h&gt;</code>. Il faudra donc que le compilateur puisse le trouver. Pour cela deux solutions :</p>
<ul>
<li style="text-align: justify;">placer le fichier d&#8217;en-tête dans <code>/usr/include</code>, <code>/usr/local/include</code> ou tout autre répertoire que <code>gcc</code> consulte &#8211; ceci doit être réservé aux fichiers cruciaux pour plusieurs applications utiles pour l&#8217;ensemble du système,</li>
<li style="text-align: justify;">laisser le fichier dans un répertoire spécifique à notre bibliothèque et indiquer à <code>gcc</code> où le trouver.</li>
</ul>
<p style="text-align: justify;">C&#8217;est naturellement la seconde option que je vais utiliser.</p>
<p style="text-align: justify;">En outre, nous ajouterons en fin de ligne l&#8217;argument <code>-lfact</code> qui demande au <em>linker</em> de réaliser l&#8217;édition des liens avec la bibliothèque <code>libfact.so</code>. Comme pour le fichier d&#8217;en-tête il faudra préciser à <code>gcc</code> où il pourra trouver le fichier <code>libfact.so</code> que nous avons créé plus haut sous forme de lien symbolique. C&#8217;est le rôle de l&#8217;option <code>-L</code>.</p>
<pre>[factorielle]$ <strong>gcc -I ./include/ -L ./lib/ -o ./test/factorielle ./test/factorielle.c -lfact</strong>
[factorielle]$ <strong></strong>
[factorielle]$ <strong>ls -l test/</strong>
total 12
-rwxrwxr-x 1 cpb cpb 7359 2012-01-27 10:41 factorielle
-rw-r--r-- 1 cpb cpb  382 2012-01-25 18:29 factorielle.c
[factorielle]$</pre>
<h2>Exécution de l&#8217;application</h2>
<p style="text-align: justify;">Si nous testons directement notre programme, son exécution échoue.</p>
<pre>$ <strong>./test/factorielle 4 5 6</strong>
./test/factorielle: error while loading shared libraries: libfact.so.1: cannot open shared object file: No such file or directory
$</pre>
<p style="text-align: justify;">En effet, l&#8217;éditeur de liens dynamique qui doit démarrer le processus ne sait pas où trouver la bibliothèque. On remarque au passage qu&#8217;il recherche bien le fichier <code>libfact.so.1</code> (avec le numéro majeur comme extension). Si notre application est suffisamment importante pour être employée régulièrement par différents utilisateurs, il est légitime de placer les fichiers de bibliothèque dans <code>/usr/local/lib</code> où le <em>linker</em> les trouvera. Toutefois si l&#8217;application est en phase de mise au point ou réservée à un emploi rare, on préférera laisser les bibliothèques dans un répertoire personnel. Dans ce cas, il faudra remplir (éventuellement dans un script de lancement) la variable d&#8217;environnement <code>LD_LIBRARY_PATH</code> pour ajouter le chemin d&#8217;accès à ces fichiers.</p>
<pre>[factorielle]$ <strong>export LD_LIBRARY_PATH=./lib/ </strong>
[factorielle]$ <strong>./test/factorielle 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorielle]$</pre>
<p style="text-align: justify;">Bien sûr, le contenu de la variable <code>LD_LIBRARY_PATH</code> peut être renseigné avec un chemin absolu plutôt que relatif si on souhaite lancer le programme exécutable depuis un emplacement quelconque.</p>
<div id="attachment_1497" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-1.png"><img class="size-medium wp-image-1497" title="Bibliothèque dynamique version 1.0" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-1-300x188.png" alt="Bibliothèque dynamique version 1.0" width="300" height="188" /></a><p class="wp-caption-text">Bibliothèque dynamique version 1.0</p></div>
<h1>Maintenance de la bibliothèque</h1>
<h2>Modification de version mineure</h2>
<p style="text-align: justify;">Notre bibliothèque semble fonctionner, nous pouvons commençer à nous livrer à des tests intensifs :</p>
<pre>[factorielle]$ <strong>./test/factorielle 3</strong>
3! = 6
[factorielle]$</pre>
<p style="padding-left: 60px;">Très bien !</p>
<pre>[factorielle]$ <strong>./test/factorielle 2</strong>
2! = 2
[factorielle]$</pre>
<p style="padding-left: 60px;">Parfait !</p>
<pre>[factorielle]$ <strong>./test/factorielle 1</strong>
1! = 1
[factorielle]$</pre>
<p style="padding-left: 60px;">Aucun souci.</p>
<pre>[factorielle]$ <strong>./test/factorielle 0</strong>
0! = 0
[factorielle]$</pre>
<p style="padding-left: 60px;">Aïe !</p>
<p style="text-align: justify;">Et oui, par convention, il est posé que <code>0! = 1</code> (vous pouvez <a title="http://fr.wikipedia.org/wiki/Factorielle" href="http://fr.wikipedia.org/wiki/Factorielle" target="_blank">vérifier sur Wikipédia</a> si vous le souhaitez). Notre programme est donc défectueux. La correction est relativement simple, il suffit de remplacer la boucle</p>
<pre>        do {
                f = f * n;
                n = n - 1;
        } while (n &gt; 1);</pre>
<p>par</p>
<pre>        while (n &gt; 1) {
                f = f * n;
                n = n - 1;
        }</pre>
<p style="text-align: justify;">C&#8217;est ce que j&#8217;ai fait dans le fichier <code><a title="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-2.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-2.c" target="_blank">fact-2.c</a></code>. En principe je devrais garder le même nom de fichier source et le remplacer simplement pour la nouvelle version de la bibliothèque. Je veux ici conserver la version précédente simplement à titre pédagogique.</p>
<p style="text-align: justify;">Je vais le compiler, puis générer une nouvelle version de bibliothèque en incrémentant le numéro mineur. L&#8217;interface de la fonction n&#8217;étant pas modifiée, les fichiers exécutables qui en dépendent doivent continuer à fonctionner normalement.</p>
<pre>[factorielle]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact-2.c</strong>
[factorielle]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.1 -o lib/libfact.so.1.1 src/fact.o</strong></pre>
<p style="text-align: justify;">Notre bibliothèque a été re-générée dans un nouveau nom de fichier, aussi faut-il relancer la commande <code>ldconfig</code>.</p>
<pre>[factorielle]$ <strong>ldconfig -n lib/</strong>
[factorielle]$ <strong>ls -l lib/</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:04 libfact.so -&gt; libfact.so.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
[factorielle]$
[factorielle]$ <strong>./test/factorielle 0 1 2</strong>
0! = 1
1! = 1
2! = 2
[factorielle]$</pre>
<p style="text-align: justify;">Notre programme fonctionne correctement pour <code>0!</code>, et l&#8217;ancienne version de la bibliothèque n&#8217;étant plus utilisée, il est possible de l&#8217;effacer.</p>
<pre>[factorielle]$ <strong>rm -f lib/libfact.so.1.0 </strong>
[factorielle]$</pre>
<div id="attachment_1498" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-2.png"><img class="size-medium wp-image-1498" title="Bibliothèque dynamique version 1.1" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-2-300x188.png" alt="Bibliothèque dynamique version 1.1" width="300" height="188" /></a><p class="wp-caption-text">Bibliothèque dynamique version 1.1</p></div>
<h2>Modification de version majeure</h2>
<p style="text-align: justify;">Après quelques essais, nous arrivons face à un nouveau problème avec notre bibliothèque.</p>
<pre>[factorielle]$ <strong>./test/factorielle -3</strong>
-3! = 1
[factorielle]$</pre>
<p style="text-align: justify;">Notre fonction renvoie une valeur lorsqu&#8217;on lui passe un nombre négatif. La véritable factorielle mathématique n&#8217;est définie que sur l&#8217;ensemble des entiers naturels, pas pour les entiers relatifs négatifs. Notre fonction devrait donc signaler l&#8217;erreur d&#8217;argument et non pas renvoyer une valeur, cohérente il est vrai mais trompeuse.</p>
<p style="text-align: justify;">Nous choisissons de modifier l&#8217;interface de notre routine, qui va prendre en argument un pointeur sur un entier <code>long long</code> dans lequel elle stockera le résultat, et renverra une valeur de réussite (zéro) ou d&#8217;échec (-1). Cette modification d&#8217;interface va impliquer une adaptation et une recompilation des applications utilisant la bibliothèque, aussi devrons-nous changer de version majeure.</p>
<p>La nouvelle fonction <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-3.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-3.c" target="_blank"><code>fact-3.c</code></a> est définie comme suit.</p>
<pre>int factorielle(long int n, long long int * result)
{
        * result = 1;
        if (n &lt; 0)
                return -1;
         do {
                 (*result) = (*result) * n;
                 n = n - 1;
         } while (n &gt; 1);
        return 0;
}</pre>
<p>Bien sur, on modifie le fichier d&#8217;en-tête en (<a title="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-3.h" href="http://www.blaess.fr/christophe/files/article-2012-01-28/fact-3.h" target="_blank"><code>fact-3.h</code></a>):</p>
<pre>#ifndef LIB_FACT_H
#define LIB_FACT_H
        int factorielle(long int n, long long int * result);
#endif</pre>
<p style="text-align: justify;">Compilons notre bibliothèque comme précédemment.</p>
<pre>[factorielle]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact-3.c</strong>
[factorielle]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 src/fact.o</strong>
[factorielle]$ <strong>ldconfig -n lib/</strong>
[factorielle]$ <strong>ls -l lib/</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:04 libfact.so -&gt; libfact.so.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:26 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:26 libfact.so.2.0
[factorielle]$ <strong>cd lib/</strong>
[lib]$ <strong>ln -sf libfact.so.2 libfact.so</strong>
[lib]$ <strong>ls -l</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:27 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:26 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:26 libfact.so.2.0
[lib]$ <strong>cd ..</strong>
[factorielle]$</pre>
<p style="text-align: justify;">Les liens sont en place pour compiler une nouvelle version du programme de test (<a title="http://www.blaess.fr/christophe/files/article-2012-01-28/factorielle-2.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/factorielle-2.c" target="_blank"><code style="text-align: justify;">factorielle-2.c</code></a>).</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fact.h&gt;

int main (int argc, char * argv[])
{
        long int n;
        long long int f;
        int i;
        if (argc &lt; 2) {
                fprintf(stderr, "usage: %s valeurs...\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        for (i = 1; i &lt; argc; i ++)
                if (sscanf(argv[i], "%ld", &amp; n) == 1) {
                        if (factorielle(n, &amp; f) == 0)
                                fprintf(stdout, "%ld! = %lld\n", n, f);
                        else
                                fprintf(stdout, "%ld! n'existe pas\n", n);
                }
        return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Compilons et essayons-le :</p>
<pre>[factorielle]$ <strong>gcc -I ./include/ -L ./lib/ -o ./test/factorielle-2 ./test/factorielle-2.c -lfact</strong>
[factorielle]$ <strong>./test/factorielle-2 3 0 -3</strong>
3! = 6
0! = 0
-3! n'existe pas
[factorielle]$</pre>
<p style="text-align: justify;">Cette fois notre programme se comporte correctement. On peut noter que la présence de l&#8217;ancienne version majeure permet à notre précédent exécutable de continuer à fonctionner.</p>
<pre>[factorielle]$ <strong>./test/factorielle 3 0 -3</strong>
3! = 6
0! = 1
-3! = 1
[factorielle]$</pre>
<div id="attachment_1499" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-3.png"><img class="size-medium wp-image-1499" title="Bibliothèque dynamique version 2.0" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-3-300x188.png" alt="Bibliothèque dynamique version 2.0" width="300" height="188" /></a><p class="wp-caption-text">Bibliothèque dynamique version 2.0</p></div>
<h1>Conclusion</h1>
<p style="text-align: justify;">La gestion des numéros majeurs et mineurs de version pour les bibliothèques dynamiques offre les avantages suivants :</p>
<ul>
<li style="text-align: justify;">Les modifications internes uniquement, représentées par des évolutions du numéro mineur, permettent aux exécutables déjà compilés de fonctionner directement avec la nouvelle version de la bibliothèque et de bénéficier &#8211; sans recompilation &#8211; des améliorations.</li>
<li style="text-align: justify;">Les transformations de l&#8217;interface externe de la bibliothèque impliquent une recompilation (éventuellement après adaptation) pour pouvoir fonctionner.</li>
</ul>
<p style="text-align: justify;">Plusieurs versions majeures de la bibliothèque peuvent cohabiter simultanément permettant un fonctionnement correct de différentes générations d&#8217;une application. Toutefois les nouvelles compilations utiliseront la version majeure pointée par le lien symbolique contenant uniquement le nom de la bibliothèque (<code>libfact.so</code>)</p>
<p style="text-align: justify;">Nous verrons dans <a title="Mise au point de bibliothèque dynamique (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/" target="_blank">le prochain article</a> comment déboguer le code de la bibliothèque dynamique en effectuant un suivi pas-à-pas de l&#8217;exécution et en examinant le contenu de ses variables.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Development of a dynamic library (1/3)</title>
		<link>http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/</link>
		<comments>http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/#comments</comments>
		<pubDate>Sat, 28 Jan 2012 08:58:13 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1502</guid>
		<description><![CDATA[(Version originale en français) During a recent training session, a conversation with a participant gave me the idea to check the options needed to perform debugging and coverage tests on a shared library. A dynamic library (libXXXX.so file &#8211; &#171;&#160;so&#160;&#187; standing for Shared Object) is loaded into memory when the process starts. The executable file [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(<a title="Mise au point de bibliothèque dynamique (1/3)" href="http://www.blaess.fr/christophe/2012/01/28/mise-au-point-de-bibliotheque-dynamique-1-compilation-versions-et-liens-symboliques/">Version originale en français</a>)</p>
<p style="text-align: justify;">During a recent training session, a conversation with a participant gave me the idea to check the options needed to perform debugging and coverage tests on a shared library.</p>
<p style="padding-left: 60px; text-align: justify;">A dynamic library (<code>lib<em>XXXX</em>.so</code> file &#8211; &laquo;&nbsp;<code>so</code>&nbsp;&raquo; standing for <em>Shared Object</em>) is loaded into memory when the process starts. The executable file and the library file are independent before launching the application, and can be maintained separately.</p>
<p style="text-align: justify;">I realized that some points were far from obvious, such as managing version numbers or activating coverage tests. Here is a list of the steps needed for the development of a dynamic library. The first article is devoted to compilation, version control and symbolic links. The <a title="Development of a dynamic library (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/development-of-a-dynamic-library-23/" target="_blank">second one</a> will focus on debugging and step-by-step tracing of the library code. The third will describe how to perform coverage tests on the library.</p>
<h1>Compiling and installing the library</h1>
<h2>Compiling library code</h2>
<p style="text-align: justify;">Let&#8217;s start by creating a small dynamic library, with a simple function: the implementation of the mathematical &laquo;&nbsp;factorial&nbsp;&raquo;.</p>
<p style="text-align: justify;">I create a working directory named <code>factorial</code> including all the files. Then we make three sub-directories:</p>
<ul>
<li style="text-align: justify;"><code>src/</code> containing the source code of the library,</li>
<li style="text-align: justify;"><code>lib/</code> where stand binary files and symbolic links described below,</li>
<li style="text-align: justify;"><code>include/</code> storing the header files of the library.</li>
</ul>
<pre>[~]$ <strong>mkdir factorial</strong>
[~]$ <strong>mkdir factorial/src</strong>
[~]$ <strong>mkdir factorial/include</strong>
[~]$ <strong>mkdir factorial/lib</strong>
[~]$ <strong>cd factorial</strong>
[factorial]$</pre>
<p style="text-align: justify;">Let&#8217;s create a file named <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact.c" target="_blank"><code>src/fact.c</code></a> containing our function.</p>
<p style="text-align: justify; padding-left: 60px;">And if you think you have found a bug in the code below, be kind and read the entire article before sending me a mocking mail <img src='http://www.blaess.fr/christophe/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<pre>#include &lt;fact.h&gt;

long long int factorial(long int n)
{
        long long int f = 1;
        do {
                f = f * n;
                n = n - 1;
        } while (n &gt; 1);
        return f;
}</pre>
<p style="text-align: justify;">This file includes its own header, which ensures consistency of the prototype and implementation.</p>
<p style="text-align: justify;">The file <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact.h" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact.h" target="_blank"><code>include/fact.h</code></a> contains the following lines.</p>
<pre>#ifndef LIB_FACT_H
#define LIB_FACT_H
    long long int factorial(long int n);
#endif</pre>
<p style="text-align: justify;">When compiling this file we will provide the following options on the <code>gcc</code> command line:</p>
<ul>
<li style="text-align: justify;"><code>-c</code> to tell <code>gcc</code> to stop his job after the compilation phase and thus providing an object file (not linking).</li>
<li style="text-align: justify;"><code>-I include/</code> telling <code>gcc</code> to look for <code>.h</code> header files in the <code>include/</code> directory in addition to the usual directories (<code>/usr/include</code>…).</li>
<li style="text-align: justify;"><code>-fPIC</code> to request the generation of a relocatable code (PIC stands for <em>Position Independent Code</em>). It is necessary for the creation of shared libraries even if this option has no effect on some architectures (x86 32-bit for example).</li>
</ul>
<p style="text-align: justify;">Here is an example of compilation:</p>
<pre>[factorial]$ <strong>ls src/</strong>
fact.c
[factorial]$ <strong>ls include/</strong>
fact.h
[factorial]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact.c</strong>
[factorial]$ <strong>ls src/</strong>
fact.c fact.o
[factorial]$</pre>
<p style="text-align: justify;">The generated <code>fact.o</code> object file will be used below.</p>
<p style="text-align: justify;">Note that during the development and testing phase, we do not use any optimization option, otherwise the compiler may change the executable code created and there won&#8217;t be an exact matching with the source file.</p>
<h2>Generation of the library</h2>
<p style="text-align: justify;">The library itself is created by invoking <code>gcc</code> with the <code>-shared</code> option. We ask him to save the library in the <code>libfact.so.1.0</code> file. The numbers 1 and 0 correspond respectively to the major and minor numbers of the library version.</p>
<p style="text-align: justify;">It is customary to consider that a major number change represents a break in binary compatibility of the library and requires recompilation of applications, while a variation of the minor number represents only internal corrections or improvements that do not interfere with the programming interface.</p>
<p style="text-align: justify;">We will tell <code>gcc</code> with the <code>-Wl</code> option to record in the heade rof the library its official name of the library including the major version number. It passes to the linker the string that follows the <code>-Wl</code> option after replacing commas by spaces. Thus the linker gets the <code>-soname libfact.so.1</code> string.</p>
<p style="text-align: justify;">It is recommanded to repeat the options passed in the previous compilation, such as <code>-fPIC</code>.</p>
<pre>[factorial]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.1 -o lib/libfact.so.1.0 src/fact.o</strong>
[factorial]$ <strong>ls lib/</strong>
libfact.so.1.0
[factorial]$</pre>
<p style="text-align: justify;">So we get the <code>libfact.so.1.0</code> file, whose header contains the <code>libfact.so.1</code> name.</p>
<h2>Creating symbolic links</h2>
<p style="text-align: justify;">When we compile an application, we tell <code>gcc</code> to link it with the <code>fact</code> library. He looks for a file named <code>libfact.so</code>, not <code>libfact.so.1.0</code>. So we have to create a symbolic link named <code>libfact.so</code> pointing to the real library file. This link is created manually using the <code>ln</code> command.</p>
<pre>[factorial]$ <strong>cd lib/</strong>
[lib]$ <strong>ln -sf libfact.so.1.0 libfact.so</strong>
[lib]$ <strong>ls -l lib*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:04 libfact.so -&gt; libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
[lib]$</pre>
<p style="text-align: justify;">During compilation <code>gcc</code> records the name of the library he used into the executable. This is the &laquo;&nbsp;official&nbsp;&raquo; name found in the <code>SONAME</code> section we filled previously with the <code>-Wl,-soname</code> option.</p>
<p style="text-align: justify;">At runtime, the loader searches the library which major number matches those used during compilation. So he has to find a file named <code>libfact.so.1</code>, or rather a symbolic link named <code>libfact.so.1</code> pointing to <code>libfact.so.1.0</code>.</p>
<p style="text-align: justify;">The creation of the first symbolic link was needed to compile an application with the library, the second link is essential to run a program associated with it. This link is used much more frequently than the first one. To make the life of the administrator easier, a command named <code>ldconfig</code> will help to automatically create the links needed to allow users to run applications. It searches the directories containing system libraries (<code>/lib</code>, <code>/usr/lib</code>, <code>/usr/local/lib</code>, etc&#8230; and all given in <code>/etc/ld.so.conf</code>) and creates links on each library file with the name contained in the <code>section</code>. Let&#8217;s see an example where I force <code>ldconfig</code> through its <code>-n</code> option to explore only our <code>lib/</code> directory.</p>
<pre>[lib]$ <strong>ldconfig -n .</strong>
[lib]$ <strong>ls -l lib*</strong>
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:04 libfact.so -&gt; libfact.so.1.0
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:05 libfact.so.1 -&gt; libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
[lib]$</pre>
<p style="text-align: justify;">The links will allow us to compile an application that requires our library (through <code>libfact.so</code>) and then run it by making sure the major version is the right one (with <code>libfact.so.1</code>).</p>
<h1>Using the library</h1>
<h2>Compiling an application</h2>
<p style="text-align: justify;">Let&#8217;s write a small program that uses our library. The <code><a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/factorial.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/factorial.c" target="_blank">factorial.c</a></code> file will call our <code>factorial()</code> function for all the numbers found on the command line.</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fact.h&gt;

int main (int argc, char * argv[])
{
        long int n;
        int i;
        if (argc &lt; 2) {
                fprintf(stderr, "usage: %s value...\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        for (i = 1; i &lt; argc; i ++)
                if (sscanf(argv[i], "%ld", &amp; n) == 1)
                        fprintf(stdout, "%ld! = %lld\n", n, factorial(n));
        return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">This file is located in the <code>factorial/test/</code> directory that we create now. It includes the <code>&lt;fact.h&gt;</code> header file. So the compiler has to find this header file. Two solutions:</p>
<ul>
<li style="text-align: justify;">Put the header file in <code>/usr/include</code>, <code>/usr/local/include</code> or any other directory where <code>gcc</code> searches. This should be reserved for critical files, needed by several applications and useful for the entire system.</li>
<li style="text-align: justify;">Keep the file in an application specific directory and tell <code>gcc</code> where to find it.</li>
</ul>
<p style="text-align: justify;">I will obviously choose the second one.</p>
<p style="text-align: justify;">In addition, we will put the <code>-lfact</code> option at the end of the command line, asking the <em>linker</em> to perform the linking with the <code>libfact.so</code> library. As for the header file, <code>gcc</code> must be told where he can find the <code>libfact.so</code> file we created previously as symbolic link. It is the role of the <code>-L</code> option.</p>
<pre>[factorial]$ <strong>gcc -I ./include/ -L ./lib/ -o ./test/factorial ./test/factorial.c -lfact</strong>
[factorial]$ <strong></strong>
[factorial]$ <strong>ls -l test/</strong>
total 12
-rwxrwxr-x 1 cpb cpb 7359 2012-01-27 10:41 factorial
-rw-r--r-- 1 cpb cpb  382 2012-01-25 18:29 factorial.c
[factorielle]$</pre>
<h2>Running the application</h2>
<p style="text-align: justify;">If we run our program directly, the execution fails.</p>
<pre>$ <strong>./test/factorial 4 5 6</strong>
./test/factorial: error while loading shared libraries: libfact.so.1: cannot open shared object file: No such file or directory
$</pre>
<p style="text-align: justify;">Indeed, the dynamic linker which should start the process does not know where to find the library. We can see that it searches for the <code>libfact.so.1</code> file (with the major number as extension). If our application is important enough to be used regularly by different users, it is legitimate to place library files in <code>/usr/local/lib</code> where the loader will find them. However if the application is currently under development or reserved for personnal use, it is preferable to leave the library in a sub-directory of our home directory. In this case, we must be fill (possibly in a startup script) the environment variable <code>LD_LIBRARY_PATH</code> to add the path to the library file.</p>
<pre>[factorial]$ <strong>export LD_LIBRARY_PATH=./lib/ </strong>
[factorial]$ <strong>./test/factorial 4 5 6</strong>
4! = 24
5! = 120
6! = 720
[factorial]$</pre>
<p style="text-align: justify;">Of course, the <code>LD_LIBRARY_PATH</code> variable can be given an absolute path rather than a relative one if you want to lauch the application from any location of the filesystem tree.</p>
<div id="attachment_1526" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-1-en.png"><img class="size-medium wp-image-1526" title="Dynamic library v. 1.0" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-1-en-300x188.png" alt="Dynamic library v. 1.0" width="300" height="188" /></a><p class="wp-caption-text">Dynamic library v. 1.0</p></div>
<h1>Maintaining the library</h1>
<h2>Minor version update</h2>
<p style="text-align: justify;">Our library seems to works, let&#8217;s engage intensive testings:</p>
<pre>[factorial]$ <strong>./test/factorial 3</strong>
3! = 6
[factorial]$</pre>
<p style="padding-left: 60px;">Very good!</p>
<pre>[factorial]$ <strong>./test/factorial 2</strong>
2! = 2
[factorial]$</pre>
<p style="padding-left: 60px;">Perfect!</p>
<pre>[factorial]$ <strong>./test/factorial 1</strong>
1! = 1
[factorial]$</pre>
<p style="padding-left: 60px;">No problem.</p>
<pre>[factorial]$ <strong>./test/factorial 0</strong>
0! = 0
[factorial]$</pre>
<p style="padding-left: 60px;">Ouch!</p>
<p style="text-align: justify;">By convention, it is hypothesized that <code>0! = 1</code> (you can <a title="http://en.wikipedia.org/wiki/Factorial" href="http://en.wikipedia.org/wiki/Factorial" target="_blank">check on Wikipedia</a> if you wish). Our program is defective. The correction is fairly simple, just replace the loop</p>
<pre>        do {
                f = f * n;
                n = n - 1;
        } while (n &gt; 1);</pre>
<p>by</p>
<pre>        while (n &gt; 1) {
                f = f * n;
                n = n - 1;
        }</pre>
<p style="text-align: justify;">That&#8217;s what I did in <code><a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact-2.c" href="http://www.blaess.fr/christophe/files/en/article-2012-01-28/fact-2.c" target="_blank">fact-2.c</a></code> file. Theoretically I should keep the same source file and replace it in the new version of the library. I wanted to keep the previous version here for demonstration purposes so did I rename the file.</p>
<p style="text-align: justify;">I will compile and generate a new library version incrementing the minor number. The interface of the <code>factorial()</code> function is not changed, executable files that depend on the library will continue to operate normally.</p>
<pre>[factorial]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact-2.c</strong>
[factorial]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.1 -o lib/libfact.so.1.1 src/fact.o</strong></pre>
<p style="text-align: justify;">Our library has been re-created with a new file name, so it&#8217;s necessary to rerun the <code>ldconfig</code> command.</p>
<pre>[factorial]$ <strong>ldconfig -n lib/</strong>
[factorial]$ <strong>ls -l lib/</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:04 libfact.so -&gt; libfact.so.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6659 2012-01-27 10:02 libfact.so.1.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
[factorial]$
[factorial]$ <strong>./test/factorial 0 1 2</strong>
0! = 1
1! = 1
2! = 2
[factorial]$</pre>
<p style="text-align: justify;">Our code works correctly for <code>0!</code>. The previous version of the library is no more used, so we can delete it</p>
<pre>[factorial]$ <strong>rm -f lib/libfact.so.1.0 </strong>
[factorial]$</pre>
<div id="attachment_1528" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-2-en.png"><img class="size-medium wp-image-1528" title="Dynamic library v. 1.1" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-2-en-300x188.png" alt="Dynamic library v. 1.1" width="300" height="188" /></a><p class="wp-caption-text">Dynamic library v. 1.1</p></div>
<h2>Major version update</h2>
<p style="text-align: justify;">After a few tests, we are facing a new problem with our library.</p>
<pre>[factorial]$ <strong>./test/factorial -3</strong>
-3! = 1
[factorial]$</pre>
<p style="text-align: justify;">Our function returns a value when given a negative number. in mathematics, the factorial is only defined for natural numbers, not for negative integers. The function should report the argument error and not return a value (coherent but misleading).</p>
<p style="text-align: justify;">We choose to modify the interface of our routine, which will take as argument a pointer to a <code>long long</code> integer where it will store the result and return a success (zero) or failure (-1) value. This change will involve an interface adaptation and recompilation of the applications using the library. So must we change the major version.</p>
<p>The new function in <a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact-3.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact-3.c" target="_blank"><code>fact-3.c</code></a> is implemented as follow:</p>
<pre>int factorial(long int n, long long int * result)
{
        * result = 1;
        if (n &lt; 0)
                return -1;
         do {
                 (*result) = (*result) * n;
                 n = n - 1;
         } while (n &gt; 1);
        return 0;
}</pre>
<p>Of course, we change the header file (<a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact-3.h" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/fact-3.h" target="_blank"><code>fact-3.h</code></a>):</p>
<pre>#ifndef LIB_FACT_H
#define LIB_FACT_H
        int factorial(long int n, long long int * result);
#endif</pre>
<p style="text-align: justify;">We compile our library as we did before:</p>
<pre>[factorial]$ <strong>gcc -c -I include/ -fPIC -Wall -o src/fact.o src/fact-3.c</strong>
[factorial]$ <strong>gcc -fPIC -shared -Wl,-soname,libfact.so.2 -o lib/libfact.so.2.0 src/fact.o</strong>
[factorial]$ <strong>ldconfig -n lib/</strong>
[factorial]$ <strong>ls -l lib/</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:04 libfact.so -&gt; libfact.so.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:26 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:26 libfact.so.2.0
[factorial]$ <strong>cd lib/</strong>
[lib]$ <strong>ln -sf libfact.so.2 libfact.so</strong>
[lib]$ <strong>ls -l</strong>
total 16
lrwxrwxrwx 1 cpb cpb   12 2012-01-27 10:27 libfact.so -&gt; libfact.so.2
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:11 libfact.so.1 -&gt; libfact.so.1.1
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:10 libfact.so.1.1
lrwxrwxrwx 1 cpb cpb   14 2012-01-27 10:26 libfact.so.2 -&gt; libfact.so.2.0
-rwxrwxr-x 1 cpb cpb 6661 2012-01-27 10:26 libfact.so.2.0
[lib]$ <strong>cd ..</strong>
[factorial]$</pre>
<p style="text-align: justify;">The links are in place to compile a new version of the test program (<a title="http://www.blaess.fr/christophe/files/article-2012-01-28/en/factorial-2.c" href="http://www.blaess.fr/christophe/files/article-2012-01-28/en/factorial-2.c" target="_blank"><code style="text-align: justify;">factorial-2.c</code></a>).</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fact.h&gt;

int main (int argc, char * argv[])
{
        long int n;
        long long int f;
        int i;
        if (argc &lt; 2) {
                fprintf(stderr, "usage: %s value...\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        for (i = 1; i &lt; argc; i ++)
                if (sscanf(argv[i], "%ld", &amp; n) == 1) {
                        if (factoriel(n, &amp; f) == 0)
                                fprintf(stdout, "%ld! = %lld\n", n, f);
                        else
                                fprintf(stdout, "%ld! doesn't exist\n", n);
                }
        return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Let us compile and try it:</p>
<pre>[factorial]$ <strong>gcc -I ./include/ -L ./lib/ -o ./test/factorial-2 ./test/factorial-2.c -lfact</strong>
[factorial]$ <strong>./test/factorial-2 3 0 -3</strong>
3! = 6
0! = 0
-3! doesn't exist
[factorial]$</pre>
<p style="text-align: justify;">This time our application behaves correctly. We can remark that the presence of the former major release enables our previous executable to continue operating.</p>
<pre>[factorial]$ <strong>./test/factorial 3 0 -3</strong>
3! = 6
0! = 1
-3! = 1
[factorial]$</pre>
<div id="attachment_1529" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-3-en.png"><img class="size-medium wp-image-1529" title="Dynamic library v. 2.0" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/figure-3-en-300x188.png" alt="Dynamic library v. 2.0" width="300" height="188" /></a><p class="wp-caption-text">Dynamic library v. 2.0</p></div>
<h1>Conclusion</h1>
<p style="text-align: justify;">The management of major and minor numbers of dynamic library versions offers the following advantages:</p>
<ul>
<li style="text-align: justify;">The internal only modifications, represented by changes in the minor number, allow already compiled executables to run directly with the new version of the library and enjoy &#8211; without recompilation &#8211; the latest improvements.</li>
<li style="text-align: justify;">Changes in the external interface of the library require a recompilation (possibly after adaptation) of the application.</li>
</ul>
<p style="text-align: justify;">Several major versions of the same library can coexist simultaneously for proper operation of different generations of an application. However, the compiler uses the new major version pointed to by the main symbolic link of the library (libfact.so)</p>
<p style="text-align: justify;">We will see in the <a title="Mise au point de bibliothèque dynamique (2/3)" href="http://www.blaess.fr/christophe/2012/02/04/mise-au-point-de-bibliotheque-dynamique-23/" target="_blank">next article</a> how to debug the code in the dynamic library, tracing it with step-by-step execution and examining the contents of its variables.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/01/28/development-of-a-dynamic-library-13/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Parallélisation de compilations</title>
		<link>http://www.blaess.fr/christophe/2012/01/14/parallelisation-de-compilations/</link>
		<comments>http://www.blaess.fr/christophe/2012/01/14/parallelisation-de-compilations/#comments</comments>
		<pubDate>Sat, 14 Jan 2012 09:05:49 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Microprocesseur]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1395</guid>
		<description><![CDATA[(English translation here) Il m&#8217;arrive très fréquemment de compiler des noyaux Linux, souvent durant des sessions de formation ou des prestations d&#8217;ingénierie (principalement dans le domaine de l&#8217;embarqué ou le développement de drivers), et parfois à titre expérimental ou par simple curiosité pour rédiger des articles ou mon prochain livre. La durée de compilation varie [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">(<a title="Parallelizing Compilations" href="http://www.blaess.fr/christophe/2012/01/14/parallelizing-compilations/">English translation here</a>)</p>
<p style="text-align: justify;">Il m&#8217;arrive très fréquemment de compiler des noyaux Linux, souvent durant des sessions de formation ou des prestations d&#8217;ingénierie (principalement dans le domaine de l&#8217;embarqué ou le développement de drivers), et parfois à titre expérimental ou par simple curiosité pour rédiger des articles ou mon prochain livre.</p>
<p style="text-align: justify;">La durée de compilation varie beaucoup en fonction de la quantité de code (de drivers, systèmes de fichiers, protocoles, etc.) et de la puissance de la machine hôte. Sur un PC de milieu de gamme, la compilation d&#8217;un kernel ajusté pour un système embarqué dure environ trois minutes. Sur une machine d&#8217;entrée de gamme (ou un peu ancienne), la compilation d&#8217;un noyau générique pour PC (disposant donc de centaines de drivers sous forme de modules) peut durer une heure.</p>
<p style="text-align: justify;">Pour tirer parti du parallélisme proposé par les processeurs actuels (systèmes multiprocesseurs, multicoeurs ou avec hyper-threading), la commande <code>make</code> nous permet de lancer simultanément plusieurs jobs. Ainsi</p>
<pre>$ <strong>make -j 4</strong></pre>
<p style="text-align: justify;">s&#8217;arrangera pour qu&#8217;il y ait toujours quatre jobs de compilation actifs.</p>
<p style="text-align: justify;">J&#8217;ai longtemps répété que &laquo;&nbsp;<em>si vous avez N processeurs (ou coeurs, ou CPU virtuels) disponibles, vous gagnerez du temps de compilation en lançant 2N jobs en parallèle</em>&laquo;&nbsp;. Ceci repose sur l&#8217;idée que pour chaque processeur nous avons un job qui effectue de la compilation (en consommant du temps CPU) et tandis qu&#8217;un autre job peut terminer de sauvegarder les résultats de la compilation précédente ou charger le fichier source du traitement suivant. Mais&#8230; est-ce vrai ?</p>
<h1>Script de test</h1>
<p style="text-align: justify;">Pour en avoir le coeur net, j&#8217;ai écrit le petit script suivant, qui télécharge au besoin les sources d&#8217;un noyau et les décompresse, puis réalise plusieurs compilations en démarrant un nombre variable de jobs.</p>
<p style="text-align: justify;">Par exemple si on lance</p>
<pre>$ <strong>./test-make-j.sh 3 5 8</strong></pre>
<p style="text-align: justify;">Il effectue trois compilations complètes : l&#8217;une avec trois tâches en parallèle, la suivante avec cinq jobs et la dernière avec huit, les résultats étant cumulés dans un fichier de texte. Le script est le suivant.</p>
<pre><a title="http://www.blaess.fr/christophe/files/article-2012-01-14/test-make-j.sh" href="http://www.blaess.fr/christophe/files/article-2012-01-14/test-make-j.sh" target="_blank"><strong>test-make-j.sh </strong></a>
#! /bin/sh

KERNEL_VERSION="linux-3.2"
KERNEL_URL_PATH="www.kernel.org/pub/linux/kernel/v3.0/"
RESULT_FILE="compilation-timing.txt"

if [ "$#" -eq 0 ]
then
  echo "usage: $@ jobs_number..." &gt;&amp; 2
  exit 0
fi

if [ ! -d "${KERNEL_VERSION}" ]
then
  if [ ! -f "${KERNEL_VERSION}.tar.bz2" ]
  then
    wget "${KERNEL_URL_PATH}/${KERNEL_VERSION}.tar.bz2"
    if [ $? -ne 0 ] || [ ! -f "${KERNEL_VERSION}.tar.bz2" ]
    then
      echo "unable to obtain ${KERNEL_VERSION} archive" &gt;&amp;2
      exit 1
    fi
  fi
  tar xjf "${KERNEL_VERSION}.tar.bz2"
  if [ $? -ne 0 ]
  then
    echo "Error while uncompressing kernel archive" &gt;&amp;2
    exit 1
  fi
fi

cd "${KERNEL_VERSION}"

echo "# Timings of ${KERNEL_VERSION} compilations" &gt;&gt; "${RESULT_FILE}"
nb_cpu=$(grep "^processor" /proc/cpuinfo | wc -l)

echo "# Processors: ${nb_cpu}" &gt;&gt; "${RESULT_FILE}"
affinity=$(taskset -p $$ | sed -e 's/^.*://') &gt;&gt; "${RESULT_FILE}"

echo "# Affinity mask: ${affinity}" &gt;&gt; "${RESULT_FILE}"
for nb in "$@"
do
  echo "# Compiling with $nb simultaneous jobs" &gt;&gt; "${RESULT_FILE}"
  <strong>make mrproper</strong>
  <strong>make i386_defconfig</strong>
  sync
  sleep 10 # Let's all calm down
  start=$(date "+%s")
  <strong>make -j $nb</strong>
  sync
  end=$(date "+%s")
  # This script will fail during february 2038 <img src='http://www.blaess.fr/christophe/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />
  echo "$nb     $((end - start))" &gt;&gt; "${RESULT_FILE}"
done</pre>
<h1>Résultats</h1>
<p style="text-align: justify;">Voici les résultats d&#8217;une exécution sur un processeur Intel Q6600 Quad-Core (fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-1.txt" href="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-1.txt" target="_blank">Intel-Q6600-1.txt</a>)</p>
<pre># Timings of linux-3.2 compilations
# Processors: 4
# Affinity mask:  f
# Compiling with 1 simultaneous jobs
1     675
# Compiling with 2 simultaneous jobs
2     346
# Compiling with 3 simultaneous jobs
3     241
# Compiling with 4 simultaneous jobs
4     197
# Compiling with 5 simultaneous jobs
5     198
# Compiling with 6 simultaneous jobs
6     194
# Compiling with 7 simultaneous jobs
7     195
# Compiling with 8 simultaneous jobs
8     196
# Compiling with 9 simultaneous jobs
9     197
# Compiling with 10 simultaneous jobs
10     198
# Compiling with 11 simultaneous jobs
11     198
# Compiling with 12 simultaneous jobs
12     198
# Compiling with 13 simultaneous jobs
13     200
# Compiling with 14 simultaneous jobs
14     201
# Compiling with 15 simultaneous jobs
15     201
# Compiling with 16 simultaneous jobs
16     200</pre>
<p style="text-align: justify;">Observons-les graphiquement avec cette petite ligne de commande pour Gnuplot. Horizontalement, nous voyons le nombre de jobs simultanés et verticalement le temps de compilation en secondes.</p>
<pre>$ <strong>echo "set terminal png size 640,480 ; set output './Intel-Q6600-1.png'; plot 'Intel-Q6600-1.txt' with linespoints" | gnuplot</strong></pre>
<p>&nbsp;</p>
<div id="attachment_1402" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-1.png"><img class="size-medium wp-image-1402" title="Intel-Q6600-1" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-1-300x225.png" alt="Compilations parallèles sur quatre CPU" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur quatre CPU</p></div>
<p style="text-align: justify;">Visiblement, les meilleurs résultats sont atteints (à quelques fluctuations près) dès <code>make -j 4</code>. Essayons de confirmer ceci. Avant de lancer le script, nous le limitons sur deux processeurs avec la commande suivante qui fixe les jobs lancés à partir du shell courant sur les processeurs 2 et 3.</p>
<pre>$ <strong>taskset -pc 2-3 $$</strong></pre>
<p style="text-align: justify;">Voici le résultat (fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-2.txt" href="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-2.txt" target="_blank">Intel-QL6600-2.txt</a>).</p>
<pre># Timings of linux-3.2 compilations
# Processors: 4
# Affinity mask:  c
# Compiling with 1 simultaneous jobs
1     684
# Compiling with 2 simultaneous jobs
2     360
# Compiling with 3 simultaneous jobs
3     362
# Compiling with 4 simultaneous jobs
4     366
# Compiling with 8 simultaneous jobs
8     370
# Compiling with 16 simultaneous jobs
16     376
# Compiling with 32 simultaneous jobs
32     377
# Compiling with 64 simultaneous jobs
64     378</pre>
<div id="attachment_1405" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-21.png"><img class="size-medium wp-image-1405" title="Intel-Q6600-2" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-21-300x225.png" alt="Compilations parallèles sur deux CPU" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur deux CPU</p></div>
<p style="text-align: justify;">Cette fois, il est visible que le minimum de temps est obtenu avec make -j 2. Si nous répétons l&#8217;expérience sur un seul CPU, on obtient les valeurs suivantes (fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-3.txt" href="http://www.blaess.fr/christophe/files/article-2012-01-14/Intel-Q6600-3.txt" target="_blank">Intel-Q6600-3.txt</a>).</p>
<pre># Timings of linux-3.2 compilations
# Processors: 4
# Affinity mask:  8
# Compiling with 1 simultaneous jobs
1     683
# Compiling with 2 simultaneous jobs
2     698
# Compiling with 3 simultaneous jobs
3     708
# Compiling with 4 simultaneous jobs
4     709
# Compiling with 5 simultaneous jobs
5     719
# Compiling with 6 simultaneous jobs
6     719
# Compiling with 7 simultaneous jobs
7     720
# Compiling with 8 simultaneous jobs
8     724</pre>
<p style="text-align: justify;">Ce qui se représente sur le graphique suivant.</p>
<div id="attachment_1406" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-3.png"><img class="size-medium wp-image-1406" title="Intel-Q6600-3" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-3-300x225.png" alt="Compilations parallèles sur un seul CPU" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur un seul CPU</p></div>
<p style="text-align: justify;">Nous pouvons regrouper ces trois courbes sur un même graphique pour mieux visualiser leurs échelles (je n&#8217;ai pas prolongé la courbe de la compilation sur un seul CPU, mais on peut imaginer qu&#8217;elle se poursuit avec une légère croissance).</p>
<div id="attachment_1448" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600.png"><img class="size-medium wp-image-1448" title="Intel-Q6600" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/Intel-Q6600-300x225.png" alt="Compilations parallèles sur processeur Q6600" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur processeur Q6600</p></div>
<p style="text-align: justify;">Pour en avoir le coeur net, nous pouvons recommencer l&#8217;expérience sur un autre processeur avec deux coeurs (AMD QL66). Les résultats sont les suivants (fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-14/AMD-QL66-1.txt" href="http://www.blaess.fr/christophe/files/article-2012-01-14/AMD-QL66-1.txt" target="_blank">AMD-QL66-1.txt</a>).</p>
<pre># Timings of linux-3.2 compilations
# Processors: 2
# Affinity mask:  3
# Compiling with 1 simultaneous jobs
1     1113
# Compiling with 2 simultaneous jobs
2     844
# Compiling with 3 simultaneous jobs
3     875
# Compiling with 4 simultaneous jobs
4     863
# Compiling with 5 simultaneous jobs
5     840
# Compiling with 6 simultaneous jobs
6     844
# Compiling with 7 simultaneous jobs
7     844
# Compiling with 8 simultaneous jobs
8     851</pre>
<div id="attachment_1407" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66-1.png"><img class="size-medium wp-image-1407 " title="AMD-QL66-1" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66-1-300x225.png" alt="Compilations parallèles sur deux CPU" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur deux CPU</p></div>
<p style="text-align: justify;">Essayons une dernière expérience, sur la même machine (deux CPU), en désactivant deux éléments :</p>
<ul>
<li style="text-align: justify;">la lecture anticipée des blocs suivants du disque (qui permet d&#8217;améliorer les lectures localisées) avec <code><strong>echo 0 &gt; /sys/block/sda/read_ahead_kb</strong></code></li>
</ul>
<ul>
<li style="text-align: justify;">l&#8217;écriture différée (de 30 secondes environ) des blocs (qui évite les accès répétitifs au disque en cas de modifications successives) avec <code><strong>mount / -o sync,remount</strong></code>.</li>
</ul>
<p>&nbsp;</p>
<p style="text-align: justify;">Cette fois les résultats sont très différents (fichier <a title="http://www.blaess.fr/christophe/files/article-2012-01-14/AMD-QL66-2.txt" href="http://www.blaess.fr/christophe/files/article-2012-01-14/AMD-QL66-2.txt" target="_blank">AMD-QL66-2.txt</a>). Les temps sont beaucoup plus longs que précédemment car à chaque écriture sur le disque, le processus attend que les données soient transmises au périphérique pour continuer son travail.</p>
<pre> Timings of linux-3.2 compilations
# Processors: 2
# Affinity mask:  3
# Compiling with 1 simultaneous jobs
1     3487
# Compiling with 2 simultaneous jobs
2     2562
# Compiling with 3 simultaneous jobs
3     2198
# Compiling with 4 simultaneous jobs
4     1963
# Compiling with 5 simultaneous jobs
5     1779
# Compiling with 6 simultaneous jobs
6     1646
# Compiling with 7 simultaneous jobs
7     1636
# Compiling with 8 simultaneous jobs
8     1602
# Compiling with 9 simultaneous jobs
9     1738
# Compiling with 10 simultaneous jobs
10     1577</pre>
<div id="attachment_1408" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66-2.png"><img class="size-medium wp-image-1408" title="AMD-QL66-2" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66-2-300x225.png" alt="Compilations parallèles sans optimisation disque" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles (2 CPU) sans optimisation disque</p></div>
<p style="text-align: justify;">Ici, la courbe est plus proche de celle que j&#8217;imaginais à l&#8217;origine. Le fait de placer plusieurs jobs par CPU permet de tirer parti des temps d&#8217;attente liés au disque pour avancer dans une autre compilation. Regroupons les deux courbes pour bien voir les durées respectives.</p>
<div id="attachment_1449" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66.png"><img class="size-medium wp-image-1449" title="Compilation parallèle sur QL66" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/01/AMD-QL66-300x225.png" alt="Compilation parallèle sur QL66" width="300" height="225" /></a><p class="wp-caption-text">Compilations parallèles sur QL66</p></div>
<h1>Conclusion</h1>
<p style="text-align: justify;">Nous voyons qu&#8217;avec la qualité de l&#8217;ordonnanceur d&#8217;entrées-sorties (<em>IO Scheduler</em>) de Linux, et la gestion optimisée des périphériques blocs, les meilleurs temps de compilation sont obtenus dès que l&#8217;on lance <strong>un job par processeur</strong>.</p>
<p style="text-align: justify;">Je modifierai donc à l&#8217;avenir ma recommandation en &laquo;&nbsp;<em>Si vous avez N processeurs disponibles, compilez votre noyau avec  </em><code>make -j N</code><em>  pour avoir le meilleur temps d&#8217;exécution</em>&laquo;&nbsp;.</p>
<p style="text-align: justify;">PS : si vous avez l&#8217;occasion de faire fonctionner ce script sur des architectures différentes (8 processeurs, 16 processeurs, etc.) je serai très intéressé par vos résultats.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/01/14/parallelisation-de-compilations/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

