Afin d’enquêter au sujet d’incidents sur les applications Java, la consigne de base est simple: dans la mesure du possible prélever un thread dump, un heap dump et si vous avez les logs du GC c’est Jackpot! Le heap dump permet de comprendre comment l’application utilise la mémoire, le thread dump donne des informations sur l’activité de l’application au moment du prélèvement, et les logs du GC sont utiles pour donner un aperçu un peu flou mais bien utile de l’activité de l’application en plus de la topologie mémoire et des occurences des collectes.
Les stats manquantes
Pour dissequer le heap dump, on a l’excellent Memory Analyzer Tool et les profilers commerciaux, pour projeter le film de l’activité du GC, il y a le non moins excellent GCViewer, mais pour le thread dump il n’y a pas grand chose. En même temps c’est du texte, donc ce n’est pas dramatique.
Toutefois certaines données me paraissent intéressantes et sont fastidieuses à extraire d’un thread dump: combien de threads? combien de threads par état? Quand un thread est bloqué en attente de l’acquisition d’un verrou sur un moniteur, quel est le thread qui le monopolise, quels sont les autres threads en attente pour verrouiller cette ressource?
Je vais développer ce nouvel outil, reste à déterminer la techno. Je veux faire du web car je trouverai toujours facilement un navigateur et je n’ai à priori pas besoin de partie serveur. L’odeur nauséabonde du JavaScript commence à agresser mes cellules olfactives…
Troll zone
Ayant commencé ma carrière en 1995, j'ai assisté à l'évolution de l'utilisation de JavaScript sans que le langage lui même ne connaisse de franche évolution. La première version a été originellement conçue en dix jours pour Netscape 2.0 et avait pour objectif d'être simple. En plus d'être un langage avec une collection de WTF!! ,pardon, de spécificités (j'affectionne tout spécialement http://martin-thoma.com/javascript-wtf/#tocAnchor-1-4), certains points me gênent particulièrement:
- il est interprété: cela signifie qu'une typo dans le code peut facilement remonter jusqu'à l'utilisateur final avant d'être détectée. La sécurité apporté par la compilation me rassure.
- il est dynamique et faiblement typé: c'est un frein à la maintenabilité lorsque la base de code du projet augmente car les refactorings sont pour le moins hasardeux
- il est pauvre car il ne contient qu'assez peu de sucre syntaxique, mais ECMA Script 6 devrait apporter un peu de fraîcheur
</ul>
</p>
Bref, dupé par son apparente simplicité, le développeur se retrouve dans un environnement où les opportunités de se prendre les pieds dans le tapis sont multiples.
C'est évidemment un mal incontournable pour du dev front web, cependant les initiatives pour éviter aux développeurs d'en écrire sont nombreuses (mais pourquoi donc?) :
- GWT
- Dart
- TypeScript
- CoffeeScript
- et ScalaJS
- d'autres que je dois oublier...
</ul>
Le code d'un langage mieux doté est "transpilé" en JavaScript, lui donnant le même rang que le bytecode de la JVM. Considérer JavaScript comme du bytecode pour navigateur me convient: la strucuture et la syntaxe du code est validée avant d'être exécutée et le niveau du langage est réhaussé.
Evidemment, c'est ma vision personnelle et cela n'enlève rien au fait que c'est une technologie tout à fait fonctionnelle... </div> ## Show me the code Comme je code actuellement quotidiennement en Scala, je me suis tourné vers ScalaJS et son écosystème. Pour ce cas d'utilisation, l'analyse syntaxique d'un thread dump, en objet pur j'aurais sûrement utilisé une conception basée sur le pattern visitor, mais la richesse du langage m'a permis de ne baser mon design que sur la pattern matching et quelques RegEx. Résultat, pour moins de 200 lignes de code, j'ai un composant chargé de transformer la représentation textuelle du thread dump en un objet strucuturé portant les informations que je souhaitais extraire : [ThreadDumpAnalyzer.scala](https://github.com/bleporini/thread-dump-analyzer/blob/master/src/main/scala/io/blep/tda/ThreadDumpAnalyzer.scala). Pour exploiter cet objet côté vue, j'ai failli opter pour retourner une version sérialisée en JSON de l'objet et gérer l'affichage du résultat en JavaScript avec JQuery, mais ça aurait ressemblé à coder un "server side" dans le navigateur, pas terrible... Au lieu de cela, je me suis reposé sur ScalaJS-dom ([Controller.scala](https://github.com/bleporini/thread-dump-analyzer/blob/master/src/main/scala/io/blep/tda/Controller.scala)) et Scala-tags ([BootstrapView.scala](https://github.com/bleporini/thread-dump-analyzer/blob/master/src/main/scala/io/blep/tda/BootstrapView.scala)). J'ai trouvé agréable d'avoir des templates typés et compilés, c'est comme avoir une sorte de HTML allégé qui se mixe parfaitement avec du code Scala: ```scala def buildThreadAccordion(groupId:String)(thread: AppThread)={ val theId: String = groupId + thread.id div(`class`:= "")( div(role:="tab")( a("data-toggle".attr := "collapse", href := s"#$theId", "data-parent".attr := s"#$groupId")( span(`class`:="leading-tab")(bulletForThreadState(thread.state)), " ", thread.name ) ), div(id := theId, `class`:="panel-collapse collapse", role:="tabpanel")( div(`class`:="panel-body")( pre(thread.stackTrace map (_ + "\n")) ) ) ) } ``` Finalement, les seules vraies lignes de JavaScript servent à gérer l'évènement de fin de l'analyse entraînant le changement d'affichage : ```javascript document.body.addEventListener("analysis_finished", function(){ $('#tabs a[href="#analysis"]').tab('show') }) ``` Pour voir le résultat, je l'ai intégré au blog (si si, regardez dans la nav du haut, à droite) et vous pouvez l'utiliser dès maintenant: [Thread Dump Analyzer](/tda.html), ou [forker le code](https://github.com/bleporini/thread-dump-analyzer) (et améliorer la partie front!): ![Thread Dump Analyzer](/assets/img/scalajs/tda.jpg) Résoudre les erreurs signalées par le navigateur sur une ligne de code JavaScript issu de code Scala transpilé n'est évidemment pas idéal en phase de développement. Heureusement le support du source mapping apporte un peu de confort dans cette situation et permet d'afficher le code Scala dans le debugger du navigateur et même d'y placer des points d'arrêt: ![Source mapping](/assets/img/scalajs/debug.jpg) ## Parce que la taille compte... Le grief principal contre les premières versions de cette technologie était la taille du JavaScript à télécharger. Malgré les améliorations, le code minifié et optimisé par le Google Closure Compiler pèse tout de même 200Ko. Le plus coûteux est le ticket d'entrée, le SDK transpilé, car l'application en elle même ne change que de peu le poids total. Notons également que lorsque le script est distribué par un serveur web utilisant la compression, la taille réduit encore (moins de 60Ko), rendant la différence avec JQuery + Bootstrap quasi anecdoditque : ![Network](/assets/img/scalajs/scripts.jpg) A titre informatif, voici la liste et la taille des scripts envoyés lorsque vous ouvrez GMail dans un navigateur: ![GMail](/assets/img/scalajs/gmail.jpg) Evidemment le périmètre fonctionnel est bien plus riche, mais je vous laisse tout de même faire l'addition et je pense qu'il est évident que la taille n'est plus un problème. ## Le mot de la fin Le langage complété par une bonne intégration dans le navigateur avec Scala Tags et ScalaJS-dom donnent une base intéressante, mais pour accoucher d'une application de la vraie vie, nous savons qu'il manque une strate: les frameworks! A ce sujet, le site de ScalaJS mentionne des adaptations à quelques frameworks dont deux permettant d'utiliser AngularJS, ça aurait été dommage de parler de front sans parler d'Angular... Bref, si vous avez déjà Scala dans votre pile technique, ScalaJS est une technologie qui devrait à minima retenir votre attention.