6

Autres outils de la chaîne de production

Nous allons étudier ici quelques uns des autres outils qui participent à la chaîne de production des programmes. Ils ne sont pas aussi indispensables que les traducteurs, l'éditeur de liens ou le chargeur, mais ils facilitent grandement le travail du programmeur lorsqu'ils sont disponibles. Leur présence sur un système particulier dépend du bon vouloir du constructeur de la machine, ou plus souvent, de la reconnaissance de leur intérêt en fonction des applications.

6.1. Les outils d'aide à la mise au point

6.1.1. La notion de trace

La mise au point d'un programme est en général une tâche difficile, et diverses méthodes ont été proposées pour aider le programmeur dans cette tâche. La première, et la plus simple, a été de lui permettre de suivre l'évolution de l'état du programme au cours du temps, c'est ce que l'on, appelle la trace du programme. Pratiquement, il n'est pas possible de suivre cette évolution sur l'état complet du programme. Il est nécessaire de restreindre ce suivi sur une petite partie de cet état. Les principales informations utiles sont les suivantes:
L'obtention de ces traces peut être définie en introduisant dans les modules sources des instructions spéciales qui sont ou non prises en compte par le traducteur en fonction d'options décidées lors de la demande de traduction. C'est alors le traducteur qui insère dans le module objet des instructions, en général d'impression, qui permettent de suivre le déroulement de l'exécution.
La difficulté essentielle de cette méthode est qu'il faut prévoir ces options au moment de la traduction du module. Il est possible que lors de l'analyse du résultat de l'exécution, il soit nécessaire de connaître d'autres informations pour déterminer l'erreur. Il faut alors refaire la traduction du module en adaptant les options de mise au point en conséquence. Pour éviter cela, la tendance est de prendre le maximum d'options, mais alors il faut dépouiller une quantité importante d'informations pour trouver celles qui sont effectivement utiles.
L'introduction des instructions de trace par l'éditeur de liens lui-même, ou par le chargeur, a été très peu utilisée, car ce n'est pas d'un emploi commode.

6.1.2. La notion de point d'arrêt, de reprise et de pas à pas

L'inconvénient majeur des options de traces traitées par le traducteur est le manque de souplesse pendant l'exécution. Lors de la recherche d'une erreur, il est souvent nécessaire de disposer de beaucoup d'informations sur l'évolution de l'état du programme au voisinage de l'endroit où se produit cette erreur. Pour faciliter le suivi de cette évolution, il suffit de pouvoir arrêter temporairement cette exécution, pour permettre l'observation de l'état complet du programme. Ceci est obtenu par le biais de points d'arrêt, qui sont des adresses d'emplacements particuliers du programme où on désire que l'exécution soit interrompue, avant que l'une des instructions situées à ces emplacements ne soit exécutée.
Lorsqu'un programme est ainsi interrompu en un point d'arrêt, le programmeur doit avoir le moyen de consulter l'état du programme, en regardant le contenu de l'espace mémoire qu'il occupe. Il doit pouvoir éventuellement modifier (à ses risques et périls) le contenu de cet espace mémoire, pour en corriger certaines valeurs. Il doit pouvoir également en reprendre l'exécution, ce que l'on appelle la reprise. On appelle metteur au point ou débogueur (en Anglais debugger) un outil interactif qui permet de créer des points d'arrêt dans un programme pour en consulter ou modifier le contenu mémoire et en reprendre ensuite l'exécution.
Le pas à pas est un cas particulier du couple <point d'arrêt, reprise>, puisqu'il s'agit, sur un programme interrompu, d'en reprendre l'exécution pour une instruction avant de l'arrêter de nouveau. Ce pas à pas peut être au niveau machine, c'est-à-dire que la reprise n'a lieu que pour exécuter une seule instruction de la machine. Pour un programmeur en langage évolué, ceci n'est guère agréable, car il n'a souvent que peu de connaissance des relations entre les instructions de la machine et les instructions en langage évolué. C'est pourquoi, on lui préfère le pas à pas au niveau langage évolué. Cependant, il faut alors que le metteur au point ait une connaissance du découpage de la suite des instructions machines en instructions en langage évolué.

6.1.3. Les metteurs au point symboliques

La définition d'un point d'arrêt doit localiser un emplacement particulier de l'espace mémoire du programme. La consultation de l'état du programme demande également de localiser les emplacements dont on veut connaître ou modifier la valeur. Cette localisation peut évidemment être donnée par une valeur numérique dans une base adaptée, octal, décimal, hexadécimal, etc... Cela demande au programmeur de connaître ces valeurs numériques. Ce sont en fait des valeurs connues de façon dispersée par les traducteurs, l'éditeur de liens et le chargeur. Pour le programmeur, elles correspondent à des identificateurs de ses modules sources. Une coopération entre l'ensemble permet d'exprimer ces adresses par les identificateurs eux-mêmes. C'est ce que l'on appelle un metteur au point symbolique.
Pour permettre cette localisation, le traducteur doit tout d'abord compléter le module objet qu'il produit par une table de symboles, qui associe aux identificateurs internes aux modules (en plus des liens) les adresses relatives des emplacements attribués aux objets correspondants. Cette table a la même signification que les liens utilisables du module. L'éditeur de liens n'utilise pas cette table, si ce n'est pour translater les adresses pour les rendre relatives au programme exécutable et non plus au module objet. Cette table associe également à chaque identificateur des informations sur le type de l'objet, pour permettre au metteur au point d'éditer (ou saisir) les valeurs des objets conformément à leur type. Cela permet aussi d'accéder aux champs d'une structure par la notation pointée habituelle aux langages de haut niveau.
Le traducteur produit enfin une deuxième table qui associe aux numéros de lignes contenant des instructions du langage de haut niveau, l'adresse relative de la première instruction en langage machine correspondant. Après translation des valeurs de cette table par l'éditeur de liens, il sera ainsi possible de définir les points d'arrêt ou le pas à pas en terme d'instructions de haut niveau, clairement identifiées par le programmeur.
Le traducteur, puis l'éditeur de liens, mettra les informations nécessaires à l'identification du module source. Le metteur au point pourra ainsi lister les lignes du texte source à la demande de l'utilisateur, lui permettant de contrôler effectivement où il en est dans le programme.
Disposant de toutes ces informations dans le fichier du programme exécutable, le metteur au point peut offrir les opérations suivantes à l'utilisateur:
  1. Possibilités de trace. Il est possible de mettre en route ou d'arrêter à tout instant des options de trace comme indiquées ci-dessus.
  2. Consultation et modification des variables du programme. Ceci permet éventuellement de corriger des valeurs erronnées et poursuivre la mise au point sans être obligé de corriger le texte source et le recompiler.
  3. Création et suppression de points d'arrêt. On peut ainsi lancer des parties d'exécution longues et s'arrêter aux endroits où l'on désire effectuer des contrôles plus précis.
  4. Exécution en pas à pas. Elle permet de contrôler plus finement le comportement du programme à certains endroits.
  5. Consultation des appels de procédure en cours. Lorsqu'un programme est interrompu, on peut ainsi savoir si on est dans le programme principal ou dans une procédure, et dans ce dernier cas, quels sont les appels imbriqués depuis le programme principal qui ont conduit à la procédure courante.
  6. Lister le fichier source. En particulier on peut ainsi savoir quelles sont les instructions de haut niveau qui seront exécutées à la reprise.
  7. Éditer le texte source. Le metteur au point peut permettre de faire appel à l'éditeur de texte pour porter des corrections au fur et à mesure dans les modules sources, et poursuivre ensuite la mise au point. Évidemment, ceci ne modifie pas le programme exécutable, les modules ainsi modifiés devant être recompilés, et l'édition de liens refaite, mais permet d'avoir un “aide-mémoire” des corrections à apporter.
Les metteurs au point offrent toujours au moins les quatre premières opérations. S'ils sont simples (on dira parfois qu'ils sont binaires), ils ne connaissent que l'organisation de la machine physique, et ne prennent pas en compte le langage d'écriture des modules qui constituent le programme. Éventuellement ils sont dotés d'un désassembleur qui permet de lister les instructions de la machine sous forme mnémonique. Ils peuvent aussi exploiter une table des symboles dans le programme exécutable, évitant à l'utilisateur de devoir définir des adresses uniquement sous forme numérique. Les metteurs au point symboliques par contre exploitent un minimum d'informations provenant du langage de haut niveau utilisé pour écrire les modules. C'est pourquoi, ils peuvent prendre en compte les structures principales de ces langages pour les quatre premières fonctions et fournir les trois fonctions supplémentaires.

6.1.4. Les metteurs au point symboliques multi-fenêtres

Lorsqu'on dispose d'un système multi-fenêtres, permettant de découper l'écran en plusieurs zones distinctes, les metteurs au point symboliques peuvent en tenir compte pour faciliter le travail de l'utilisateur. Trois fenêtres sont alors le plus souvent utilisées:
La manipulation du metteur au point par l'utilisateur en est alors grandement facilitée, puisque certaines commandes sont implicites et qu'il a devant les yeux les informations essentielles pour cette mise au point.

6.1.5. Les mesures de comportement dynamique

Certains systèmes offrent la possibilité de faire des mesures sur le comportement dynamique d'un programme à l'exécution. En général, deux types de mesures sont possibles:
Ces mesures peuvent être effectuées sur la totalité du programme, ou sur certains modules spécifiques. Elles sont obtenues, au moyen d'une option de traduction, qui demande au traducteur d'introduire des instructions aux endroits judicieux du programme. Par exemple, l'incrémentation d'un compteur avant l'exécution d'une instruction en langage évolué permet de compter le nombre de ses exécutions; l'appel à une fonction système spécifique permet d'obtenir les mesures de temps d'exécution. Ces mesures peuvent être plus ou moins précises, suivant les caractéristiques du matériel. Les résultats sont mémorisés dans un fichier, au fur et à mesure, et peuvent être exploités par un outil spécifique qui rattache les mesures aux instructions des modules sources correspondants.
Multics permet de faire ces mesures au niveau de chaque instruction des langages évolués, grâce à une horloge très précise (de l'ordre de la microseconde). Mais on les trouve plus souvent au niveau des appels de sous-programmes, car la durée d'exécution plus importante exige une précision moins grande des mesures de durées.
L'intérêt de ces mesures réside surtout dans l'amélioration ultérieure de l'efficacité du programme par l'utilisateur, qui sait alors exactement où il doit porter son effort. Il est, en effet, inutile de gagner quelques microsecondes sur une instruction qui est exécutée une seule fois, alors que l'on peut être surpris de la durée effective d'une instruction banale, mais qui fait intervenir des conversions coûteuses.

6.2. Les préprocesseurs et les macrogénérateurs

Il arrive souvent que l'on ait besoin de disposer d'un module source sous différentes versions assez proches les unes des autres, sans que la fonctionnalité et les spécifications du module soient changées. C'est le cas, par exemple, lorsque le module utilise des constantes d'adaptation qui permettent de configurer les tailles des zones de données internes. C'est le cas également lorsque le module est intégré à une application qui doit pouvoir être portée sur des machines et systèmes différents, entraînant des variantes mineures de ce module. Si on dispose d'autant de copies de ce module source qu'il y a de versions, on rencontre deux problèmes: le premier est le nombre de ces copies presque identiques, et le second est la maintenance de toutes ces copies, lorsqu'une modification doit être apportée à toutes les versions.
Certains langages de programmation, ou leur traducteur, intègrent la prise en compte de ces différentes versions. Cependant une autre solution consiste à disposer d'un seul exemplaire du module source qui est alors paramétré pour permettre la construction d'une version particulière immédiatement avant la traduction. C'est le rôle d'un programme spécifique appellé préprocesseur. L'intérêt de reporter au niveau d'un préprocesseur ce travail, au lieu de le laisser au traducteur, est que, d'une part, le préprocesseur n'est pas attaché à un langage particulier, et peut donc servir à beaucoup de langages, d'autre part, cela simplifie le traducteur.
Nous allons nous baser sur le préprocesseur construit par les concepteurs du langage C, pour expliquer succintement le fonctionnement général. L'idée est qu'un préprocesseur prend en entrée un module source, et fournit en sortie un module source transformé. Il s'agit donc bien d'un outil qui travaille au niveau texte source. Il doit être capable de distinguer dans ce texte source les commandes qui lui sont destinées du reste du texte. Dans le cas du préprocesseur de C, on les distingue par la présence du caractère "#" en début de ligne. Ces commandes doivent permettre des modifications simples du reste du texte. En voici les principales:
#include "nom de fichier"
demande au préprocesseur d'inclure à cet endroit le fichier correspondant.
#define TOTO 2456
demande au préprocesseur de remplacer systématiquement, à partir de maintenant la chaîne de caractères TOTO par 2456 partout où elle se trouve.
#undef TOTO
demande au préprocesseur d'arrêter le remplacement systématique de la chaîne TOTO.
#ifdef TOTO

 °°° 
#else

 °°° 
#endif
demande au préprocesseur de transmettre les lignes qui suivent la commande #ifdef, s'il y a une demande de remplacement en cours pour la chaîne TOTO, et de les supprimer s'il n'y en a pas. La prochaine commande #else, si elle existe, entraînera l'action opposée (suppression ou transmission) pour les lignes qui suivent, et la commande #endif reprendra le cours de fonctionnement normal. Ces commandes peuvent être imbriquées, avec la signification habituelle des langages de haut niveau.
Il faut remarquer que le langage de définition des commandes ainsi que les opérations de remplacement dans le texte n'impliquent aucune relation avec le langage C lui-même. Ce préprocesseur est d'ailleurs communément utilisé pour le langage Pascal dans les systèmes Unix.
Les remplacements définis ci-dessus sont rudimentaires. En fait ils peuvent être paramétrés. Ainsi la commande
	#define NUMERO(A, B) 25 * A + B
demande le remplacement des chaînes de la forme NUMERO (u, v), où u et v sont des chaînes quelconques ne contenant pas de virgules, par la chaîne 25 * u + v. On dit parfois que l'on a une macrodéfinition. Il est important de rappeler que le préprocesseur travaille au niveau chaîne de caractères, sans avoir de connaissance du langage représenté. C'est à l'utilisateur de prendre garde que la chaîne obtenue après remplacement soit conforme à ce qu'il désire. Par exemple, dans le contexte de la commande précédente,
	NUMERO(x + 1, y)		deviendra	 	25 * x + 1 + y
	NUMERO((x + 1), y)		deviendra		25 * (x + 1) + y
Il est probable que le premier remplacement n'est pas celui désiré. Il serait en fait préférable d'écrire:
	#define NUMERO(A, B) 25 * (A) + (B)
pour que l'interprétation du remplacement obtenu conserve la structure syntaxique 25 * A + B, quels que soient A et B.
Plus généralement on parlera de macrogénérateur pour un outil qui a pour but de produire un module source dans un langage de programmation donné, à partir d'un module source qui mélange des commandes dans le langage du macrogénérateur, parfois appellé méta-langage, et des parties dans ce langage de programmation. L'une des utilisations les plus courantes est de faciliter l'interfaçage d'un programme d'application avec une base de données. Le programmeur exprimera ses requêtes à la base de données dans le méta-langage, le macrogénérateur traduira ces requêtes en une suite d'instructions du langage de programmation, souvent constituées d'appels à des sous-programmes spécifiques.

6.3. Le make

Nous avons vu qu'un programme était constitué de plusieurs modules. Mais chacun de ces modules peut lui-même servir à construire plusieurs programmes. Lorsque le nombre de modules impliqués dans un ensemble de programmes est important, il devient difficile de maîtriser complètement les conséquences d'une modification d'un module particulier sur les programmes exécutables. Plus généralement, le problème est d'être certain que les programmes exécutables reflètent bien toutes les modifications qui ont été apportées dans les modules sources. Une solution est évidemment de refaire la traduction de tous les modules, et de refaire ensuite l'édition de liens de tous les programmes. Ceci peut entraîner des traductions inutiles, et des éditions de liens inutiles. Le make est un programme qui résout ce problème en ne faisant que ce qui est nécessaire, sous réserve que les informations qu'il exploite soient cohérentes. Initialement écrit pour le système Unix, de nombreuses versions existent maintenant pour des systèmes variés.

6.3.1. Graphe de dépendance

Pour déterminer ce qui doit être fait, make utilise trois sources d'informations:
À partir de ces informations, il construit d'abord un graphe de dépendance sur l'ensemble des fichiers qui servent à la construction du fichier cible que l'on cherche à obtenir, et qui est fourni en paramètre à make. Considérons par exemple un programme exécutable appellé prog, obtenu par édition de liens des modules objets situés dans les fichiers x.o, y.o et z.o. On dit que prog dépend de ces fichiers. De même, si le module objet x.o est obtenu par compilation d'un module source situé dans le fichier x.c, avec inclusion du fichier source defs, on dit que x.o dépend de x.c et de defs. Remarquons que la compilation de x.c demandant l'inclusion de defs n'entraîne pas de dépendance entre x.c et defs, puisque x.c n'est pas construit à partir de defs. Par contre, il y a bien dépendance de x.o sur defs, puisqu'une modification de defs nécessite de reconstruire x.o. La figure 6.1 représente le graphe de dépendance complet.

Fig. 6.1. Graphe de dépendance.
Les dépendances peuvent être directement décrites dans le fichier makefile, et c'est un de ses rôles. On pourrait ainsi mettre dans ce fichier les lignes suivantes:
	prog : x.o y.o z.o
	x.o y.o : defs
Certaines dépendances peuvent être aussi obtenues au moyen de règles implicites simples et naturelles. Prenons comme convention (c'est assez habituel, et nous l'avons déjà fait), que les modules objets, résultats d'une traduction, sont dans des fichiers dont le nom est le nom du module suffixé par .o; ces fichiers dépendent évidemment du fichier qui contient le module source. Il est naturel, et également courant, de donner au fichier source le nom du module avec un suffixe lié au langage dans lequel il est écrit, comme par exemple .c pour le langage C, .p pour la langage pascal, ou .f pour le Fortran. Make peut alors chercher à utiliser les noms des fichiers du répertoire courant pour compléter les règles de dépendances. Ainsi, pour le fichier x.o, il recherche dans le répertoire un fichier dont le nom commence par x et suffixé par l'un de ceux correspondant à un langage source. Le fait de trouver le fichier x.c, lui permet d'établir la dépendance correspondante.

6.3.2. Exploitation du graphe de dépendance

Pour déterminer si un fichier est à jour, c'est-à-dire reflète les dernières modifications, il suffit de contrôler que chaque fichier existe et a une date de dernière modification supérieure ou égale à celle des fichiers dont il dépend. Si c'est le cas pour tous les fichiers du graphe, rien n'est à faire, et le fichier cible est à jour. Si ce n'est pas le cas, il est nécessaire de reconstruire ceux qui ne vérifient pas cette propriété. Il est nécessaire de faire cette reconstruction depuis le bas du graphe en remontant vers le haut, puisque la reconstruction d'un fichier entraîne celle de tous ceux qui dépendent de lui et qui sont donc situés au dessus.
Le problème est alors de savoir comment on peut reconstruire un fichier à partir de ceux dont il dépend. Pour cela, il est possible d'attacher à une règle de dépendance dans le fichier makefile un ensemble de commandes dont l'exécution sera demandée au système par make lorsque la règle ne sera pas respectée. De même, aux règles implicites sont attachées une ou plusieurs commandes qui jouent le même rôle. Pour notre exemple, le fichier makefile pourrait associer à la règle relative à prog la commande d'appel de l'édition de liens avec l'utilisation de la bibliothèque de nom s, et dont le résultat serait mis dans le fichier prog:
	prog : x.o y.o z.o
		ld x.o y.o z.o -ls -o prog
	x.o y.o : defs
La commande associée à la règle implicite de dépendance du suffixe .o sur .c pourrait être l'appel du traducteur correspondant sur le fichier source.

6.3.3. Macro substitution

Pour faciliter l'écriture du fichier makefile, make propose un mécanisme simple de macro substitution. Une définition de macro est simplement une ligne contenant un nom suivi du signe "=" et du reste de la ligne. Une référence à une macro est constituée du signe "$" suivi du nom de la macro entre parenthèses s'il fait plus d'un caractère. Par ailleurs, avant de lancer des commandes relatives à une règle de dépendance, make initialise certaines macros, comme par exemple $? qui représente la chaîne des noms de fichiers qui ont été trouvés plus récents que le fichier cible, et qui sont donc la cause de cette exécution.
Dans le contexte du fichier makefile de la figure 6.2, l'exécution de
	make prog
entraîne la construction du graphe de dépendance donné en figure 6.1, et les recompilations éventuellement nécessaires ainsi que l'édition de liens. Le nom de l'éditeur de liens est paramétré par $(LD), implicitement identifié à ld, pour permettre sa redéfinition lors de l'appel de make. De même, la définition des bibliothèques utilisées lors de cette édition de liens est paramétrée explicitement par la définition de la macro LIBES.
		OBJECTS =	x.o y.o z.o
		FILES = x.c y.c z.c defs
		LIBES = -ls
		P = imprint -Plplaser
		prog : $(OBJECTS)
			$(LD) $(OBJECTS) $(LIBES) -o prog
		x.o y.o : defs
		print : $(FILES)
			$P $?
			touch print
		arch :
			ar uv /usr/src/prog.a $(FILES)
Fig. 6.2. Exemple de fichier makefile.
Pour dater certaines opérations, il est possible de créer des fichiers vides, et de demander au système de modifier la date de leur dernière modification lorsque l'opération est exécutée (commande touch avec Unix, par exemple). Supposons qu'il existe un tel fichier de nom print, dans le contexte du même fichier makefile, l'exécution de
	make print
entraînera l'impression des fichiers qui sont plus récents que ce fichier. Les commandes associées à la règle de dépendance non seulement impriment ceux qui sont plus récents, mais force la date du fichier print pour mémoriser la date à laquelle cette opération a été faite.
Enfin, après construction du graphe de dépendance, si un fichier n'existe pas, le make exécute les commandes associées à la règle de dépendances qui lui correspond. En général, ces commandes entraînent sa construction effective. Cependant, si le fichier n'est pas créé, le make considère qu'il a la date actuelle vis-à-vis des règles de dépendances au-dessus de lui. Il n'est donc pas obligatoire qu'un fichier soit effectivement attaché aux opérations. Ainsi, dans le contexte de l'exemple, l'exécution de
	make arch
entraîne toujours l'exécution des commandes associées, puisque le fichier arch n'existe pas. Lorsque la commande est exécutée, le fichier n'est toujours pas créé, mais make considère qu'il est daté du moment présent, et donc que les propriétés d'antériorité sont respectées.

6.4. Les outils complémentaires

Il est courant de trouver sur la plupart des installations, des outils d'aide à la gestion des modules.
Ce peut être, par exemple, un archiveur qui permet d'archiver des modules sources, ou plus généralement des fichiers quelconques. Leur intérêt est d'une part de regrouper ensemble des fichiers qui forment un tout cohérent, d'autre part de diminuer l'encombrement sur support secondaire de cet ensemble, par le biais de techniques complexes de compression d'informations. Le résultat est souvent au moins deux fois moins encombrant.
Pour faciliter le suivi de l'évolution d'un module source, on peut disposer d'un programme, appellé diff, qui détermine la différence entre deux fichiers de texte. Le résultat est la suite de commandes d'édition qui permet à un éditeur orienté lignes, de reconstruire le deuxième fichier à partir du premier. On voit que ceci permet à un programmeur de diffuser les modifications à apporter à une version du source pour passer à la version suivante. Celui qui reçoit le résultat du diff, n'a pas à faire les modifications à la main, mais à les faire exécuter par l'éditeur orienté lignes correspondant.
La présentation externe d'un module source est souvent très importante pour la mise au point et la maintenance. Un paragrapheur, encore appellé indenteur, est un programme qui donne une forme standard à un fichier source écrit dans un langage donné. Il est adapté au langage lui-même, car cette forme ne peut être obtenue que par une analyse lexicale et surtout syntaxique du texte source, pour en reconnaître les structures fondamentales.
La gestion des modules objets et des bibliothèques de ces modules est souvent assurée par un bibliothéquaire. Nous avons vu qu'une telle biblothèque de modules pouvait nécessiter une structure de données pour rendre plus efficace le travail de l'éditeur de liens.
De nombreux autres outils existent. Nous ne pouvons les décrire tous, certains étant dédiés à des tâches très précises. La raison est que souvent lorsque l'on doit faire ponctuellement une activité automatisable, il est préférable de faire l'outil qui réalisera cette activité plutôt que de faire l'activité soi-même, car l'outil pourra resservir.

6.5. Conclusion

+ La trace d'un programme est une suite d'informations qui permettent de contrôler l'évolution de l'état d'un programme au cours de son exécution. En fait on ne cherche bien souvent à connaître qu'une partie de cette trace.
+ Un point d'arrêt dans un programme est un endroit où l'on désire arrêter l'exécution du programme pour consulter son état interne. La reprise est la possibilité de poursuivre l'exécution du programme après qu'il ait été interrompu. Le pas à pas est la possibilité d'exécuter le programme une instruction à la fois.
+ Un metteur au point est un outil qui permet de contrôler interactivement l'exécution d'un programme. Il est symbolique lorsqu'il permet l'expression des commandes en utilisant les structures du langage évolué dans lequel le programme a été écrit.
+ Les mesures de comportement dynamique d'un programme permettent de savoir où il faut porter son effort pour améliorer son temps d'exécution.
+ Un préprocesseur ou un macrogénérateur est un programme qui permet de faire un traitement simple sur le texte source avant de le donner à un traducteur.
+ Il existe un graphe de dépendance entre l'ensemble des fichiers utilisés dans la chaîne de production de programmes: un fichier A dépend d'un fichier B si B est utilisé pour construire A.
+ Un fichier contenant un programme exécutable est à jour si pour tous les fichiers A et B de son graphe de dépendance, tels que A dépende de B, la date de dernière modification de A est postérieure à celle de B.
+ Le make est un outil qui, à l'aide d'un fichier parmètre, construit le graphe de dépendance d'un programme exécutable, contrôle qu'il est à jour et exécute les commandes minimales pour assurer cette mise à jour, si ce n'est pas le cas.
+ Le make peut être utilisé chaque fois qu'il existe un graphe de dépendance entre des fichiers, pour contrôler que ces fichiers sont à jour, et exécuter les commandes nécessaires à cette mise à jour, si ce n'est pas le cas.
+ Beaucoup d'autres outils existent, car il est souvent préférable de faire un outil pour une activité automatisable, plutôt que de réaliser l'activité à la main.