Upload
dangnguyet
View
217
Download
0
Embed Size (px)
Citation preview
Bruno [email protected]
IFT6315 - Analyse et compréhension de programmes
Analyse dynamiqueIntroduction
Bruno Dufour - Université de Montréal
Rappel - Analyse dynamique
• L’analyse dynamique permet:• d’obtenir des résultats plus précis pour une ou
plusieurs exécutions concrètes• d’obtenir de l’information de nature temporelle à
propos de l’exécution• d’obtenir de l’information sur la fréquence ou
l’importance de certains événements• Ex : une méthode est appelée très souvent,
beaucoup de tableaux sont créés, etc.
3
Bruno Dufour - Université de Montréal
Comparaison des techniques 4
• Considère toutes les exécutions possibles
• Calculs complexes mais sans impact sur l’exécution
• Considère certaines exécutions concrètes, généralisation difficile
• Impact sur l’exécution proportionnel à la quantité d’information recueillie
Analyse statique Analyse dynamique
Bruno Dufour - Université de Montréal
Approximation du comportement 5
Analyse statique sûre (safe)
Analysedynamique
Comportement réel
Analyse statiquenon-sûre (unsafe)
Bruno Dufour - Université de Montréal
Utilité de l’analyse dynamique
• Compréhension de programmes• Détection de phases d’exécution• Analyse de l’utilisation des ressources
• Analyse de performance• Analyse de l’utilisation de la mémoire
• Optimisations JIT• Débogage• Analyses de tainte (sécurtité)• Analyse de couverture• Détection d’invariants probables• etc.
6
Bruno Dufour - Université de Montréal
Analyse dynamique - préoccupations • Comment recueillir l’information ?
• Transformations du code source / compilé (instrumentation)
• Modifications de l’environnement d’exécution • Utilisation d’une interface de profilage
• Comment éviter (minimiser) le surcoût?• Comment s’assurer que les résultats ne sont pas
affectés par les observations ?• ex: changements au comportement du système
• Comment choisir les entrées / exécutions representatives ?
• Comment s’assurer de la validité des résultats?
7
Bruno Dufour - Université de Montréal
Interface de profilage 8
• Fourni un mécanisme d’inspection du programme en cours d’exécution
• Permet de s’arrimer, par exemple, à une machine virtuelle pour intercepter des événements ou contrôler l’exécution
• Exemples :• Java Virtual Machine Tool Interface (JVMTI) • Valgrind• Émulateurs• Program counters (CPU)• etc.
Bruno Dufour - Université de Montréal
Interface de profilage 9
JVM JVMTI
Agent de profilage
class%loading
method%entry
method%exit
classestransformées
Programme
Événements
Callbacks
Bruno Dufour - Université de Montréal
Environnement d’exécution modifiés 10
• Pour les langages interprétés, il est souvent possible de modifier un interprete existant afin de collecter l’information désirée
• Exemples :• JikesRVM (aka Jalapeno VM) - JVM de recherche
écrite en Java• aussi SableVM, Maxine, ...
• Modifications d’interprete commerciaux (e.g. V8 pour JS, ...)
Bruno Dufour - Université de Montréal
Instrumentation
• L’instrumentation consiste à ajouter des fragments de code (sondes, ou probes) à un programme de façon à ajouter la génération des résultats d’analyse à son comportement initial
• Le programme s’exécute dans son environnement normal
• Souvent nécessaire, e.g. WebSphere est distribué avec sa version de J9 (IBM JVM)
• Au cours de l’exécution, les sondes sont exécutées en plus du programme d’origine recueillent l’information nécessaire
11
Bruno Dufour - Université de Montréal
public'static'int[]%append(int%i,%int[]%a)%{%%%%int[]%r;
%%%%%%%%
%%%%%%%%
%%%%%%%%
%%%%
}
if%(a%!=%null)%{%%%%%%%%%%%%r%=%new%int[a.length%+%1];%%%%%%%%%%%%System.arraycopy(a,%0,%r,%1,%a.length);}%else%{%%%%%%%%%%%%r%=%new%int[1];}r[0]%=%i;
%%%%return%r;
Instrumentation 12
Analysis.recordEntry(“append”);
Analysis.recordAlloc(“int[]”,%a.length%+%1);
Analysis.recordCall(“System.arraycopy”);
Analysis.recordAlloc(“int[]”,%1);
Analysis.recordExit(“append”);
Bruno Dufour - Université de Montréal
public'static'int[]%append(int%i,%int[]%a)%{%%%%int[]%r;
%%%%%%%%
%%%%%%%%
%%%%%%%%
%%%%
}
%%%%try%{%%%%%%%%
%%%%%%%%
%%%%%%%%
%%%%
%%%%}%finally%{%%%%%%%%Analysis.recordExit(“append”);%%%%}
if%(a%!=%null)%{%%%%%%%%%%%%r%=%new%int[a.length%+%1];%%%%%%%%%%%%System.arraycopy(a,%0,%r,%1,%a.length);}%else%{%%%%%%%%%%%%r%=%new%int[1];}r[0]%=%i;
%%%%return%r;
Instrumentation 13
Analysis.recordEntry(“append”);
Analysis.recordAlloc(“int[]”,%a.length%+%1);
Analysis.recordCall(“System.arraycopy”);
Analysis.recordAlloc(“int[]”,%1);
Analysis.recordExit(“append”);
Bruno Dufour - Université de Montréal
Instrumentation
• Types d’instrumentation• Code source (source-to-source) : transformations du
code source (haut niveau)• Code compilé (ex: bytecode)
• Stratégie• Hors-ligne : instrumenter avant de débuter l’exécution
• génère une nouvelle version du programme instrumenté
• ex : AspectJ, Soot, etc.• En-ligne : instrumenter durant l’exécution
• le code est transformé à mesure qu’il est chargé par l’environnement d’exécution
• ex: valgrind, Steamloom, Dyko, etc.
14
Bruno Dufour - Université de Montréal
Instrumentation en-ligne
• Comment intercepter le chargement du code ?• En Java, plusieurs stratégies :
• JVMTI : permet d’intercepter le chargement de toutes les classes (sauf les tableaux)
• java.lang.Instrument : permet d’intercepter le chargement de certaines classes (au moins toutes les classes de l’application)
• ClassLoader personnalisé : permet de charger des classes dans un ClassLoader spécifique et de les modifier avant le chargement
• Modification de la JVM• Modifications du code d’entrée/sortie (I/O)
15
Bruno Dufour - Université de Montréal
Pièges de l’instrumentation 16
• Le code inséré par instrumentation ne doit pas appeler du code instrumenté sans une garde
• Possibilité de causer de la récursion sans borne• Attention aux modifications dans java.lang.Object!
• Certaines classes sont difficiles à instrumenter• Java : Object, String, tableaux, etc.
• Toutes les classes utilisées ne sont pas forcément disponibles à l’avance
• Java : Proxy génère des classes à l’exécution!• Certaines méthodes (ex: native) ne peuvent pas être
instrumentées
Bruno Dufour - Université de Montréal
Récursion infinie... 17
public%class%Object%{%%%%public%Object()%{%%%%%%%%Analysis.recordEntry(“Object.Object()”);%%%%}}
public%class%Analysis%{%%%%public%static%void%recordEntry(String%method)%{%%%%%%%%Node%n%=%new%Node(method);%%%%%%%%...%%%%}}
Oops! Va appeler Object(), puis Node(), puis Object(), etc.
Bruno Dufour - Université de Montréal
Proxy - exemple 18
public interface Foo { public Object bar(Object obj);}
public class FooImpl implements Foo { public Object bar(Object obj) { return obj; }}
Source: Oracle (avec lègères modifications)
Bruno Dufour - Université de Montréal
Proxy - exemple 19
public class TracingProxy implements InvocationHandler { private Object obj;
public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new TracingProxy(obj)); }
private TracingProxy(Object obj) { this.obj = obj; }
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } finally { System.out.println("after method " + m.getName()); } return result; }}
Source: Oracle (avec lègères modifications)Bruno Dufour - Université de Montréal
Proxy - exemple 20
public static void main(String[] args) { Foo foo = (Foo) TracingProxy.newInstance(new FooImpl());
foo.bar(null); }}
before method barafter method bar
Le type de l’objet Foo ici est $Proxy0, une classe générée durant l’exécution (et qui implémente l’interface Foo, et délègue à FooImpl).
Source: Oracle (avec lègères modifications)
Bruno Dufour - Université de Montréal
Clonage des classes 21
• Technique consistant à dupliquer systématiquement toutes les classes d’un système de façon à faciliter l’instrumentation :
Source: Factor et. al., “Instrumentation of Standard Libraries in Object-OrientedLanguages: the Twin Class Hierarchy Approach”, OOPSLA’04
Bruno Dufour - Université de Montréal
Clonage des classes
• Permet d’instrumenter les classes difficiles ou impossibles à instrumenter de façon portable
• Les classes copiées peuvent être modifiées sans restriction
• L’application s’exécute uniquement dans la copie• Le code original est disponible à partir du code
instrumenté• Permet d’éviter certaines sources d’interférence
entre le code d’instrumentation et le code de l’application
22
Bruno Dufour - Université de Montréal
Pièges du clonage des classes
• classes/interfaces spéciales (Serializable, Thread, Exception, etc.)• class MyException extends TCH.java.lang.Exception
• Reflexion (Class.forName)• Class.forName(“MyClass”) → “TCH.MyClass”
• Transformations illégales• public String toString() → public TCH.String toString()
• Exceptions implicites• try { ... } catch (NullPointerException e) →
try { ... } catch (TCH.NullPointerException e)
• Méthodes “native”• System.currentTimeMillis() →
TCH.System.currentTimeMillis()
23
Bruno Dufour - Université de Montréal
Collecte de l’information 24
InterfacesModifications
de l’environnement
Instrumentation
Information disponible
Limitée par l’interface Compléte Accessible à
l’application
Difficulté Facile Complexe Moyenne
Impact Moyen à élevé Minimal Minimal à moyen
Bruno Dufour - Université de Montréal
Perturbations de l’exécution 25
• L’analyse dynamique peut perturber l’exécution de façon
• directe : le comportement de code d’analyse peut directement affecter l’information recueillie
• ex: le temps d’exécution mesuré inclut le temps passé à exécuter les sondes
• indirecte : le code d’analyse affecte indirectement le comportement mesuré/observé du système
• ex: invalidation de la mémoire cache cause un ralentissement, ordonnancement différent des fils d’exécution (threads)
Bruno Dufour - Université de Montréal
Types d’analyse dynamique
• En ligne (Online)• Les résultats sont calculés au cours de l’exécution• Des calculs complexes peuvent perturber
l’exécution• Seulement l’information pertinente est enregistrée
• Hors ligne (Offline)• Les résultats sont calculés après l’exécution (post-
mortem) à l’aide de traces d’exécution• L’impact du calcul sur l’exécution est diminué• La quantité d’information à enregistrer peut être
énorme (et proportionnelle au temps d’exécution)
26
Bruno Dufour - Université de Montréal
Précision de l’analyse dynamique
• Analyse exacte• Enregistre toute l’information pertinente lors d’une
exécution• Analyse par échantillonnage
• Enregistre un échantillon de l’exécution périodiquement
• Deux stratégies de base• Par interruption / minuteur : un échantillon pour
chaque période (ex: 100ms)• Par compteurs : un échantillon à chaque N
occurrences (ex: 1 échantillon à chaque 10 appels)
27
Bruno Dufour - Université de Montréal
Échantillonnage par compteurs 28
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Back-edge retourne au code d’origine
if%(globalCounter%<=%0)%{%%%%takeSample();%%%%globalCounter%=%resetValue;}globalCounterQQ;
Condition d’échantillonnage :
Bruno Dufour - Université de Montréal
Échantillonnage par compteurs 29
• Le framework permet d’utiliser l’échantillonnage pour plusieurs types d’analyses dynamiques
• Le code est dupliqué, avec une version instrumentée (instrumented) et une version qui ne contient que des gardes (checking)
• Les gardes sont insérées à chaque entrée de méthode et sur chaque back-edge
• Si la condition de profilage est vraie, la garde branche au code instrumenté
• Les back-edges retournent au code non-instrumenté pour borner le temps passé dans le code instrumenté
• Implique que le CFG instrumenté est un DAG
Bruno Dufour - Université de Montréal
Variation - Moins de duplication 30
• top-node : noeud non-instrumenté pour lequel aucun chemin depuis l’entrée ne contient de noeud instrumenté
• les gardes qui branchent à un top-node doivent être retirées
• pour chaque arc depuis un top-node vers un noeud instrumenté, on doit ajouter une garde sur l’arc correspondant dans le code non-instrumenté
• bottom-node : noeud non-instrumenté depuis lequel aucun autre noeud instrumenté n’est atteignable
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Retirer plus de noeuds ?• Peut augmenter le nombre de gardes nécessaires :
• En ne retirant que les top-nodes et bottom-nodes, le nombre de gardes est borné par le nombre initial (et peut diminuer dans certains cas)
31
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Alternative - Aucune duplication 32
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Résultats - Performance 33
Variation-0 = Duplication complèteVariation-2 = Aucune duplication
* indique une différence ≤ 0.5%
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Résultats - Précision 34
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Résultats détaillés - javac 35
Precision = 93.8%
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Alternative - Par interruptions 36
Precision
intervale = 10ms intervale = 30 000
Source: Arnold & Ryder, “A Framework for Reducing the Cost of Instrumented Code”, PLDI’01
Bruno Dufour - Université de Montréal
Problèmes de l’échantionnage 37
void!M()!{!!!!!!!!while!(...)!{!!!!!!!!getfield!//!Long!sequence!of!non6calls
!!!!!!!!putfield
!!!!!!!!...
!!!!!!!!getfield
!!!!!!!!putfield
!!!!!!!!call_1();!//!Two!short!calls
!!!!!!!!call_2();
!!!!}
}
Source: Arnold & Grove, “Collecting and Exploiting High-Accuracy Call Graph Profiles in Virtual Machines”, CGO’05
Une stratégie naïve peut manquer ces appels à cause de la faible probabilité de prendre un échantillon lors de leur exécution.
Bruno Dufour - Université de Montréal
Échantillonnage hybride 38
Source: Arnold & Grove, “Collecting and Exploiting High-Accuracy Call Graph Profiles in Virtual Machines”, CGO’05
Bruno Dufour - Université de Montréal
Profilage des chemins d’exécution 39
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
• Chaque chemin est encodé par un numéro unique entre 0 et le nombre de chemins possibles
• pour l’instant, nous allons ignorer les cycles• Les arcs sont instrumentés de façon à produire le numéro du
chemin emprunté dans un registre, qui peut alors être utilisé comme index dans une table de profilage
• Simples opérations arithmétiques sur un sous-ensemble des branches
Bruno Dufour - Université de Montréal
Approche 40
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
Choisir l’emplacement des sondes 41
• En général, plusieurs options possibles• solution (a) requiert seulement au plus 2 calculs par chemin, (b)
et (c) en requièrent plus• Hypothèse : il existe au préalable un ensemble de poids sur les
arcs qui représentent la fréquence d’exécution attendueSource: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
Étapes de l’algorithme 42
• Assigner les numéros à chaque chemin de façon unique
• Utiliser un arbre couvrant (spanning tree) pour sélectionner les arcs à instrumenter
• Sélectionner l’instrumentation appropriée• Dériver le chemin après l’exécution depuis son
numéro
Bruno Dufour - Université de Montréal
1. Assigner les numéros 43
ENTRY
EXIT
Arc ajouté pour des raisons techniques (étape 2)
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96Bruno Dufour - Université de Montréal
2. Sélection des arcs 44
• De façon informelle, tente de “pousser” les calculs sur les cordes (arcs qui n’appartiennent pas à l’arbre couvrant) sans changer les valeurs pour les chemins empruntés
• Considère le cycle créé en ajoutant chaque corde à l’arbre couvrant
• Calcule Inc(e) en utilisant la somme des valeurs des arcs composant le cycle (négation dans le sens inverse de l’arc)
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
3. Sélection de l’instrumentation 45
• Approche de base• [r = 0] : Initialisation à l’entrée (ENTRY)• [r += Inc(c)] : Mise à jour pour chaque corde c
instrumentée• [count[r]++] : Enregistrement du profil à la sortie
(EXIT)• Optimisation pour une corde c :
• [r=Inc(c)] ssi c est la première corde sur chaque chemin de ENTRY à EXIT contenant c
• [count[r + Inc(c)]++] ssi c est la dernière corde sur chaque chemin de ENTRY à EXIT contenant c
Bruno Dufour - Université de Montréal
3. Sélection de l’instrumentation 46
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
4. Dériver un chemin 47
R = 3
R = 1
derivePath(R) v ← ENTRY while v != EXIT find e = v → w with maximum Val(e) ≤ R v ← w R ← R - Val(e)
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
• Pour chaque back-edge v → w, ajouter un arc ENTRY → v et un arc w → EXIT
• Eliminer les back-edges du graphe et appliquer l’algorithme précédent
• Instrumenter chaque back-edge avec [count[r]++; r = 0]• Le cas v = w doit être traité spécialement
Bruno Dufour - Université de Montréal
Gestion des cycles 48
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
Gestion des cycles - Exemple 49
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96Bruno Dufour - Université de Montréal
Évaluation - Performance 50
Source: Ball & Larus, “Efficient Path Profiling”, MICRO’96
Bruno Dufour - Université de Montréal
Analyse de la mémoire
• Identification des fuites de mémoire (memory leaks)• Analyse d’objets de tas (heap)
• Nombre/taille/etc des objets• Classification de la mémoire utilisée• Connectivité/structure des objets• etc.
51
Bruno Dufour - Université de Montréal
Utilisation des octets 52
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07
1"tableau"(surdimensionné)"contenant1"objet"String"et"ses"4"caractères
Bruno Dufour - Université de Montréal
Rôle des octets dans les objets 53
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07Bruno Dufour - Université de Montréal
Agglomération d’objets en structures 54
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07
Têtes"de"collec9ons
Tableau"deréférencesEntrée"de"liste
Objets"contenus
Bruno Dufour - Université de Montréal
Rôle des objets dans les collections 55
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07Bruno Dufour - Université de Montréal
Le rôle des octets dans les collections 56
+
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07
Bruno Dufour - Université de Montréal
La signature de « santé mémoire » 57
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07Bruno Dufour - Université de Montréal
Classfication des octets 58
Données réelles
Coûts dûs à la machine virtuelle
Coûts d’agglomération
Coûts dûs aux pointeurs
Coûts dûs aux valeurs primitives
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07
Bruno Dufour - Université de Montréal
Résultats 59
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07Bruno Dufour - Université de Montréal
Évolution et échelle 60
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07
Bruno Dufour - Université de Montréal
Collection de collections d’octets 61Linked
List
TLinkedList
Array
101 102 103 104Taille"interne
Source: N. Mitchell & G. Sevitsky, “The Causes of Bloat, The Limits of Health”, OOPSLA’07Bruno Dufour - Université de Montréal
Analyses JIT
• JIT = Just-in-time (juste à temps)• L’interprétation pure du code est trop lente• Les interpréteurs modernes effectuent de la
compilation durant l’exécution• Les compilateurs JIT utilisent de l’information
dynamique pour guider les optimisations (feedback-directed optimisations, ou FDO) qui permet :
• de sélectionner les optimisations les plus profitables• d’effectuer des optimisations qui ne sont pas
possibles statiquement• d’invalider certaines optimisations si la situation
change
62
Bruno Dufour - Université de Montréal
Exemples d’optimisations de JIT
• Incorporation de méthodes (inlining): permet d’éviter un appel en remplaçant l’appel par le code de la méthode appelée
• Restructuration du code: utilise l’information sur la fréquence d’exécution pour maximiser la localité sur le chemin le plus souvent emprunté
• Versionnement du code: génère des versions spécialisées d’un même fragment de code à utiliser dans différents contextes
• et bien d’autres…
63
Bruno Dufour - Université de Montréal
Profilage et JITs 64
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
• Inlining seulement appliqué aux méthodes courtes (code plus court que la séquence d’appel côté appelant)
• Propagation de constantes / copies de base
• Inlining plus agressif
• Analyse par flot de données
• Passe additionnelle pour la génération de code
• Analyse d’échappement (escape analysis)
• Allocation sur la pile (stack allocation)
• Ordonnancement des instructions
Bruno Dufour - Université de Montréal
Inlining - stratégie statique 65
• Pour un budget donné, rejeter une méthode pour incorporation si
• le nombre de variables résultant de l’incorporation excède un seuil, ou
• la taille estimée du code résultant excède un seuil, ou
• la taille estimée du code de la méthode à incorporer dépasse un seuil, ou
• évite de passer tout le temps disponible sur une longue méthode
• l’appel n’a jamais été exécuté et n’est pas contenu dans une boucle
Bruno Dufour - Université de Montréal
Impact sur le temps de compilation 66
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
La taille du code cause une augmentation significative du temps d’analyse
Bruno Dufour - Université de Montréal
Impact sur la performance 67
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
~50% du gain total
Bruno Dufour - Université de Montréal
Inlining - stratégie par profilage 68
1. Identification de la distribution des sites d’appels• Construit un graphe d’appels partiel à partir des
méthodes exécutées fréquemment (hot)• Calcule un ratio d’importance pour chaque arc en
fonction du site d’appel enregistré
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
Bruno Dufour - Université de Montréal
Inlining - stratégie par profilage 69
2. Identification des chemin pour incorporation• Traverse les chemins en sens inverse (à partir des feuilles)• La fréquence (hotness) du parent est ajustée pour tenir
compte de l’incorporation des enfants en fonction des ratios calculés en 1
• Une portion de la contribution d’un enfant est ajoutée au parent
• Ces ratios sont obtenus par instrumentation sélective3. Requête d’incorporation
• Pour les chemins fréquents identifiés en 1 & 2, une requête est placée pour l’incorporation
• Les requêtes sont persistantes pour éviter les oscillations
Bruno Dufour - Université de Montréal
Inlining - stratégie par profilage 70
4. Requêtes de recompilation• La liste de méthodes fréquemment exécutées est
traversée à nouveau pour identifier des opportunités de recompilation
• Certaines méthodes peuvent maintenant être sous le seuil à cause des réajustements en 3 et seront rejetées
• Les méthodes pour lesquelles un seul site d’appel existe et qui seront la cible d’inlining seront aussi rejetées
Bruno Dufour - Université de Montréal
Identifier les méthodes courtes
• Problème : le profilage par échantillonnage peut facilement manquer certains courtes méthodes, qui sont les meilleurs cibles pour l’incorporation
• Le JIT identifie et incorpore aussi les “petites” méthodes
• La taille estimée de leur code compilé est inférieure au code nécessaire pour effectuer l’appel dans l’appelant et l’appelé (prologue, épilogue et séquence d’instructions au site d’appel).
71
Bruno Dufour - Université de Montréal
Évaluation - Stratégies
• Static• Profilage statistique mais aucune instrumentation• Stratégie statique employée
• Profile-1• Stratégie statique non utilisée (sauf pour les très petites et petites
méthodes)• Stratégie conservatrice (toutes les méthodes incorporées doivent
apparaître dans la liste des méthodes fréquemment exécutées)• Profile-2
• Version plus aggressive de Profile-1• Hybrid
• Profile-1, puis Static tant que le budget n’est pas épuisé• Offline
• Profile-1, mais le profil est collecté au préalable
72
Bruno Dufour - Université de Montréal
Évaluation - Temps de compilation 73
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
Bruno Dufour - Université de Montréal
Évaluation - Taille du code 74
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
Bruno Dufour - Université de Montréal
Évaluation - Performance 75
Source: Suganuma et al, “An Empirical Study of Method Inlining for a Java Just-In-Time Compiler”, JVM’02
Structures d’appels
Bruno Dufour - Université de Montréal
Traces d’exécution
• Enregistre certains événements survenus lors de l’exécution
• Exécution d’instructions / blocs de base / branches• Appels de méthodes• Lecture / écriture de valeurs en mémoire• Etc.
• Une trace peut contenir plusieurs types d’événements• Peut facilement atteindre plusieurs GB pour des
exécutions relativement courtes• La nature des traces permet de développer des
techniques de compression très efficaces
77
Bruno Dufour - Université de Montréal
Arbre d’appels
• Une trace d’exécution contient habituellement au moins les appels effectués lors de l’exécution
• souvent entrées / sorties de méthodes• peut contenir d’autres événements en plus des
appels• Une trace peut facilement être convertie en un arbre
d’appels• Représente tous les appels de façon hiérarchique• Conceptuellement équivalent à un graphe d’appels
qui utilise un contexte distinct par invocation d’une méthode
78
Bruno Dufour - Université de Montréal
Arbre d’appels 79
Source: Zhuang et. al., “Accurate, Efficient, and Adaptive Calling Context Profiling”, PLDI’06
ENTER%AENTER%BENTER%CENTER%ELEAVE%EENTER%ELEAVE%ELEAVE%CLEAVE%BENTER%DENTER%CLEAVE%CLEAVE%DENTER%BENTER%CENTER%ELEAVE%EENTER%ELEAVE%ELEAVE%CLEAVE%BENTER%DENTER%CENTER%ALEAVE%ALEAVE%CLEAVE%DLEAVE%A
Trace:
Bruno Dufour - Université de Montréal
Graphe d’appels dynamique
• Un graphe d’appels dynamique représente les appels entre méthodes effectués lors d’une ou plusieurs exécutions concrètes
• Comme dans un graphe d’appels statique, les noeuds représentent les méthodes et les arcs les appels entre méthodes
• Peut être obtenu par analyse spécialisée ou par agrégation d’un arbre d’appels
80
Bruno Dufour - Université de Montréal
Graphe d’appels 81
Source: Zhuang et. al., “Accurate, Efficient, and Adaptive Calling Context Profiling”, PLDI’06
Bruno Dufour - Université de Montréal
Arbre de contextes d’appels
• Un arbre de contextes d’appels (Calling Context Tree, ou CCT)[Ammons et al, PLDI’97] est une représentation des appels plus compacte qu’un arbre d’appels mais plus précise qu’un simple graphe d’appels
• L’arbre est agrégé par chaîne d’appels (invocation dans son contexte d’appel) et non par méthode comme pour un graphe
• En général, plus qu’un noeud par méthode
82
Bruno Dufour - Université de Montréal
Arbre de contextes d’appels (CCT) 83
Source: Zhuang et. al., “Accurate, Efficient, and Adaptive Calling Context Profiling”, PLDI’06
Bruno Dufour - Université de Montréal
Construction d’un CCT à l’exécution 84
• Il est aussi possible de construire un CCT directement durant l’exécution (et non par agrégation d’un arbre)
• Considérons un appel de D par C• s’il existe déjà un noeud pour D dans les enfants de C,
ce noeud sera passé à D lors de l’appel et réutilisé• sinon, D est appelée pour la première fois dans ce
contexte• rechercher un noeud pour C dans les ancêtres de C.
Si un tel noeud est trouvé, D est un appel récursif et le noeud existant est réutilisé.
• sinon, créer un nouveau noeud pour D et l’ajouter comme enfant de C.
• La chaîne des parents n’est traversée que lors du premier appel à une méthode dans un contexte donné.
Bruno Dufour - Université de Montréal
Construction du CCT - Performance 85
Source: Ammons et al, “Exploiting Hardware Performance Counters with Flow and Context Sensitive Profiling”, PLDI’97
Bruno Dufour - Université de Montréal
Performance - Algo. similaire en Java 86
Source: Sarimbekov et al, “Complete and Platform-Independent Calling Context Profiling for the Java Virtual Machine”, ENTCS’11
Couverture
Bruno Dufour - Université de Montréal
Analyse dynamique et couverture
• Dans certains cas, l’analyse dynamique sert à étudier une exécution précise
• Débogage, performance, etc.• Autrement, on doit s’assurer que les résultats sont
représentatifs• Couverture du code adéquate• Entrées représentatives d’après le comportement
attendu du programme
88
Bruno Dufour - Université de Montréal
Couverture
• La couverture est une mesure d’exhaustivité du code touché par une ou plusieurs exécutions concrètes
• Idéalement, la couverture devrait être exprimée en fonction de tous les chemins d’exécution possibles
• En pratique, le nombre de chemins possibles est trop grand pour être utilisé.
• On utilise des approximations.
89
Bruno Dufour - Université de Montréal
Couverture des instructions
• Nécessite que chaque instruction du programme soit exécutée au moins une fois
• Un critère de couverture assez faible :public!int!foo(int!x,!int!y)!{
!!!!if!(x!!=!0!&&!y!!=!0)
!!!!!!!!y=x+1;
!!!!return!y;
}
• L’entrée foo(1,1) exécute toutes les instructions, mais le cas où y est retourné sans modification n’est jamais exécuté.
90
Bruno Dufour - Université de Montréal
Couverture des décisions / branches
• Nécessite que chaque décision prenne chaque valeur possible au moins un fois (en général vrai ou faux)
• Les conditions incluent les boucles (while, for, do-while, etc.), les instructions conditionnelles (if-else, switch)
• Implique la couverture des instructionspublic int foo(int x, int y) {
if (x != 0 && y != 0)
y=x+1;
return y; }• Il faudrait au moins 2 entrées, par exemple :
• foo(0, 1), foo(1, 1)
91
Bruno Dufour - Université de Montréal
Couverture des conditions
• Nécessite que chaque condition qui fait partie d’une décision prenne chaque valeur possible au moins un fois (en général vrai ou faux)public int foo(int x, int y) {
if (x != 0 && y != 0)
y=x+1;
return y;
}• Il faudrait au moins 2 entrées, par exemple :
• foo(0, 0)• foo(1, 1)
92
Bruno Dufour - Université de Montréal
Couverture des conditions
• La couverture de conditions n’implique pas la couverture de branches :public int foo(int x, int y) {
if (x != 0 && y != 0)
y = x+1;
return y;
}• Considérez les entrées suivantes :
• foo(0, 1)• foo(1, 0)
• Chaque condition prend les deux valeurs possibles, mais l’instruction ‘y = x+1’ n’est jamais exécutée
93
Bruno Dufour - Université de Montréal
Couverture des branches/conditions
• Requiert que chaque décision prenne toutes les valeurs possibles, et que chaque condition d’une décision prenne toutes les valeurs possibles
• Combinaisons des deux approches précédentes• Problème : certaines valeurs peuvent en masquer
d’autrespublic int foo(int x, int y, int z) {
if (x != 0 && (y != 0 || z < 0))
y=x+1;
return y;
}• Pour l’entrée foo(0,1,1), y!=0 peut masquer z<0
94
Bruno Dufour - Université de Montréal
Couverture de conditions multiples
• Requiert que toutes les combinaisons de conditions dans toutes les décision soient couvertes au moins une fois :
• Implique toutes les approches précédentespublic int foo(int x, int y) {
if (x != 0 && y != 0)
y=x+1;
return y;
}• Il faudrait au moins 4 entrées, par exemple :
• foo(0,0), foo(1,0), foo(0,1), foo(1,1)
95
Bruno Dufour - Université de Montréal
Où trouver les entrées?• Définir des entrées manuellement
• Limité à des programmes simples• Utiliser les tests de l’application
• Permettent l’analyse dynamique de bibliothèques (aucun point d’entrée)
• Enregistrer des entrées lors d’exécutions réelles et les rejouer
• Générer des entrées au hasard• “fuzzing”• random test generation (ex: Randoop)
• Exploration dirigée (ex: concolic execution : DART, Cute, etc.)
96
Génération de tests aléatoires
Bruno Dufour - Université de Montréal
Génération de tests aléatoires
• Idée générale :• Générer des entrées aléatoirement• Vérifier que le programme se comporte
correctement pour ces entrées• Plusieurs études ont montré l’efficacité des tests
aléatoires peut être comparable à d’autres techniques plus coûteuses dans certains cas
• les évaluations se concentrent généralement sur de petits programmes et mesurent la couverture du code
98
Bruno Dufour - Université de Montréal
Randoop• Feedback-directed random testing
• Pacheco et al, “Feedback-directed Random Test Generation”, ICSE’07
• Permet d’éviter les tests redondants et illégaux
99
Set!s!=!new!HashSet();
s.add(“abc”);
assertTrue(s.equals(s));
Date!d!=!new!Date(2012,!2,!27);
assertTrue(d.equals(d));
Date!d!=!new!Date(2012,!2,!27);
d.setMonth(61);
assertTrue(d.equals(d));
Date!d!=!new!Date(2012,!2,!27);
d.setMonth(61);
d.setDay(5);
assertTrue(d.equals(d));
Set!s!=!new!HashSet();
s.add(“abc”);
s.isEmpty();
assertTrue(s.equals(s));
Illégal
Illégal
UtileUtile
Redondant
Bruno Dufour - Université de Montréal
Randoop - Approche
• Construire des tests unitaires incrémentalement à partir de séquences de méthodes
• Exécuter les séquences aussitôt qu’elles sont générées (feedback)
• permet de détecter les séquences invalides• permet de favoriser les séquences qui génèrent de
nouveaux états pour les objets créés
100
Bruno Dufour - Université de Montréal
Randoop - Approche 101
Source: Pacheco & Ernst, “Randoop: Feedback-Directed Random Testing for Java”, OOPSLA’07
Bruno Dufour - Université de Montréal
Randoop - Contrats 102
• Contrats de méthodes• Pas de NullPointerException lancée
lorsqu’aucun paramètre n’était null• Pas de AssertionError lancée
• Contrats d’objets• o.equals(o) retourne vrai• o.equals(o) ne lance pas d’exception• o.hashCode() ne lance pas d’exception• o.toString() ne lance pas d’exception
• D’autres contrats peuvent être spécifiés par l’utilisateur
Bruno Dufour - Université de Montréal
Randoop - Exemple de contrat violé 103
public-static-void!test1()!{!!!LinkedList!l1!=!new!LinkedList();!!!Object!o1!=!new!Object();!!!l1.addFirst(o1);
!!!TreeSet!t1!=!new!TreeSet(l1);!!!Set!s1!=!Collections.unmodifiableSet(t1);
!!!Assert.assertTrue(s1.equals(s1));!//"oops!!}
Bruno Dufour - Université de Montréal
Randoop - Algorithme 104
• while time limit has not been reached• Create a new sequence snew
• Randomly pick a public method m(T1...Tk):Tret
• For each Ti, pick a value vi for the parameter‣ Ti is primitive : pick at random from a predefined set
(-1, 0, 1, ‘a’, true, etc.)‣ Ti is reference :
- Pick a value from an existing sequence- Add a new sequence and pick its value- Pick null
• Create new sequence snew = s1; ...; sk; vnew = m(v1...vk)• if snew was previously constructed, continue
• Classify the sequence snew
Bruno Dufour - Université de Montréal
Randoop - Classification
• Violation de contrat ?• La séquence est rapportée à l’utilisateur
• Séquence redondante ?• La séquence est éliminée• Enregistre l’ensemble de tous les objets créés durant
la génération de séquences• Une séquence est redondante si tous les objets créés
lors de son exécution sont présents dans cet ensemble (d’après equals)
• Sinon, la séquence est ajoutée à la liste de séquences disponibles pour la génération
• Des filtres sont appliqués pour éviter d’étendre des séquences invalides
105
Bruno Dufour - Université de Montréal
Randoop - Filtres
• Exceptions : élimine les tests qui génèrent des exceptions (sans violer de contrat) avant la fin de l’exécution
• Souvent une indication d’une précondition non-respectée
• Null : élimine les tests qui utilisent null comme valeur intermédiaire
106
Object!o!=!new!Object();
LinkedList!l!=!new!LinkedList();
l.add(null);!//!null!statique
Object!o!=!returnNull();!//!null!dynamique
LinkedList!l!=!new!LinkedList();
l.add(o);
Bruno Dufour - Université de Montréal
OCAT
• Jaygarl et al, “OCAT: Object Capture based Automated Testing”, ISSTA’10
• Extension de Randoop qui utilise de vrais objets capturés lors d’exécutions normales
• D’autres objets sont générés aléatoirement comme pour Randoop
• Les objets peuvent aussi être mutés pour augmenter la couverture
107
Exploration dirigée
Bruno Dufour - Université de Montréal
Exécution symbolique
• Généralisation de l’exécution traditionnelle (concrète)• Concept clé : permettre l’utilisation de valeurs
symboliques• Chaque entrée du programme est représentée par
une valeur symbolique (ex: α)• Les variables du programme vont recevoir des
valeurs calculées à partir de constantes ou de valeurs symboliques
• La sémantique des opérations du langage est étendue pour inclure les valeurs symboliques
• ex: les expressions manipulent des polynômes contenant des valeurs symboliques
109
Bruno Dufour - Université de Montréal
Exemple 110
function!sum(a,!b,!c)!{!!!!var!x!=!a!+!b;
!!!!var!y!=!b!+!c;
!!!!var-z!=!x!+!y!6!b;
!!!!return!z;}
a b c x y z
1 3 5
4
8
9
Bruno Dufour - Université de Montréal
Exemple 111
function!sum(a,!b,!c)!{!!!!var!x!=!a!+!b;
!!!!var!y!=!b!+!c;
!!!!var-z!=!x!+!y!6!b;
!!!!return!z;}
a b c x y z
α β γ
α+β
β+γ
α+β+γ!
Bruno Dufour - Université de Montréal
État et conditions
• L’état d’un programme au cours d’une exécution inclut
• le compteur d’instruction (instruction en cours)• les valeurs de toutes les variables
• Après un branchement conditionnel (ex: if), l’état devra aussi contenir une condition de chemin (path condition), ou pc
• pc : condition booléenne• dépend seulement des valeurs symboliques (et non
pas des variables du programme)• conjonction d’expressions symboliques• initialement true, et raffinée au cours l’exécution
112
Bruno Dufour - Université de Montréal
Branchements conditionnels
• La condition d’un branchement est une expression booléenne q = R ≥ 0, où R est un polynôme de valeurs symboliques
• L’évaluation de la condition d’une branche modifie la valeur de pc
• branche then : pc ← pc ⋀ q• branche else : pc ← pc ⋀ ¬q
• L’évaluation de chaque branche procède indépendamment avec la valeur de pc modifiée
• Intuitivement, un “fork” dans l’exécution• Les exécutions forment un arbre
113
✔
Bruno Dufour - Université de Montréal
Exemple 114
function!f(a,!b,!c)!{!!!!var!x=0,!y=0,!z=0;
!!!!if-(a)!!!!!!!!x!=!62;
!!!!if-(b!<!5)!!!!!!!!if!(!a!&&!c)!!!!!!!!!!!!y!=!1;
!!!!!!!!z!=!2;
!!!!assert-(x+y+z!!=!3);}
a = α, b = β, c = γ,x = 0, y = 0, z = 0
α ¬α
x = -2
β < 5 β ≥ 5
z = 2
✔
α ⋀ (β < 5)
✔
α ⋀ (β < 5)
β < 5 β ≥ 5
✔
¬α ⋀ (β ≥ 5)¬α ⋀ γ ¬α ⋀ ¬γ
z = 2y = 1
✘
z = 2
¬α ⋀ (β < 5) ⋀ ¬γ
¬α ⋀ (β < 5) ⋀ γ
Bruno Dufour - Université de Montréal
Implémentation 115
• Les solutionneurs SMT/SAT modernes permettent d’implémenter l’exécution symbolique de façon efficace
• Les implémentations utilisent souvent des heuristiques pour améliorer la performance
• ex: rechercher une paire p et ¬p pour identifier rapidement les contraintes sans solution
• Les implémentations doivent aussi faire certaines hypothèses quant à la nature des valeurs et expressions générées
• ex: expressions linéaires, polynômes, etc.
Bruno Dufour - Université de Montréal
DART - Exploration dirigée
• DART = Directed Automated Random Testing• Godefroid et al, “DART : Directed Automated
Random Testing”, PLDI’05• Idée principale
• Générer des entrées aléatoires pour un programme• Exécuter le programme avec les entrées concrètes
tout en accumulant des contraintes symboliques sur le chemin emprunté
• Utiliser les contraintes pour déterminer d’autres entrées qui permettent d’emprunter les chemins non-explorées
116
Bruno Dufour - Université de Montréal
DART - Exemple 117
int!double(int!x)!{!! return!2!*!x;!}
void!test_me(int!x,!int!y)!{!!int!z!=!double(x);
!!if!(z==y)!{! !!if!(y!==!x+10)! !!!!abort();!/*!error!*/
!!}
}
main(){% int%v1%=%randomInt();% int%v2%=%randomInt();% test_me(v1,v2);}
Étape 2 : Génération d’un driver
Étape 1 : Extraction d’interface• Arguments passés au point
d’entrée (spécifié par l’utilisateur)• Variables et fonctions externes
Bruno Dufour - Université de Montréal
DART - Exemple 118
int!double(int!x)!{!! return!2!*!x;!}
void!test_me(int!x,!int!y)!{!!int!z!=!double(x);!!if!(z==y)!{! !!if!(y!==!x+10)! !!!!abort();!/*!error!*/
!!}
}
x = 36y = 99
Valeurs concrètes
Contraintes symboliques
z == 2 * x
Étape 3 : Exploration dirigée
z != y
Trouver une solution à y = 2 * xex: x=1, y=2
Exécution 1
z = 72
Bruno Dufour - Université de Montréal
DART - Exemple 119
int!double(int!x)!{!! return!2!*!x;!}
void!test_me(int!x,!int!y)!{!!int!z!=!double(x);!!if!(z==y)!{! !!if!(y!==!x+10)! !!!!abort();!/*!error!*/
!!}
}
x = 1y = 2
Valeurs concrètes
Contraintes symboliques
z == 2 * x
Étape 3 : Exploration dirigée
z == y
Trouver une solution à y = 2 * x ⋀ y == x + 10ex: x=10, y=20
Exécution 2
z = 2
y != x + 10
Bruno Dufour - Université de Montréal
DART - Exemple 120
int!double(int!x)!{!! return!2!*!x;!}
void!test_me(int!x,!int!y)!{!!int!z!=!double(x);!!if!(z==y)!{! !!if!(y!==!x+10)! !!!!abort();!/*!error!*/
!!}
}
x = 10y = 20
Valeurs concrètes
Contraintes symboliques
z == 2 * x
Étape 3 : Exploration dirigée
z == y
Exécution 2
z = 20
y == x + 10Erreur détectée!
Bruno Dufour - Université de Montréal
Exécution dirigée 121
• Permet de diriger systématiquement l’exécution vers certains chemins
• Basée sur la collecte de contraintes symboliques pour chacune des branches
• Une contrainte est inversée pour forcer l’exécution de l’autre alternative d’une branche
• Un solutionneur est utilisé pour les entrées pour la prochaine exécution
• Le processus est répété jusqu’à ce que tous les chemins soient couverts
• Peut ne jamais terminer, il faut donc limiter l’exploration dans la plupart des cas
Bruno Dufour - Université de Montréal
Contraintes complexes• DART fait l’hypothèse que seules les contraintes linéaires
peuvent être résoluesvoid!foo(int!x,int!y)!{
!!!!int!z!=!x*x*x;
!!!!if!(z!==!y)!{
!!!!!!!!abort();!/*!error!*/
!!!!}
}
• Initialement, x = 3, y = 7• z = 27 (concret) et z = x3 (abstrait)• La valeur de z ne peut être gérée de façon abstraite• La valeur concrète (27) est utilisée• Lors de la prochaine exécution, y = 27 et l’erreur est
atteinte
122
Bruno Dufour - Université de Montréal
Contraintes complexes (2)
• Certaines contraintes ne peuvent être résolues statiquement :void!obscure(int!x,!int!y)!{!!!if!(x!==!hash(y))!abort();!!!return!0;}
• Il n’est pas possible de générer deux valeurs x et y statiquement tel que x == hash(y).
• Par contre, il est facile de générer une valeur pour x si on fixe la valeur de y
• DART peut donc explorer les deux branches de la fonction obscure en variant la valeur de x
123