Tutoriel: Afficher les allocations par chemin d'appel

Tutoriel: Afficher les allocations par chemin d'appel

Cette page montre comment utiliser l'API du Debugger pour afficher combien d'objets une page web alloue, triés par le chemin d'appel qui les a alloués.

Note : le nouvel outil de développement Memoire permet de faire ce genre de choses.

  1. Visitez l'URL : about:config, et passez la préférence devtools.chrome.enabled à true :

    Setting the 'devtools.chrome.enabled' preference
     
  2. Ouvrez une ardoise JavaScript (menu > Développement > Ardoise JavaScript), et sélectionnez "Navigateur" depuis le menu "Environment". (Ce menu ne sera PAS présent si vous n'avez pas changé la préférence à l'étape 1 ).

    Selecting the 'browser' context in the Scratchpad

     

  3. Entez le code suivant dans l'ardoise :

    // This simply defines the 'Debugger' constructor in this
    // Scratchpad; it doesn't actually start debugging anything.
    Components.utils.import('resource://gre/modules/jsdebugger.jsm');
    addDebuggerToGlobal(window);
    
    (function () {
      // The debugger we'll use to observe a tab's allocation.
      var dbg;
    
      // Start measuring the selected tab's main window's memory
      // consumption. This function is available in the browser
      // console.
      window.demoTrackAllocations = function() {
        dbg = new Debugger;
    
        // This makes hacking on the demo *much* more
        // pleasant.
        dbg.uncaughtExceptionHook = handleUncaughtException;
    
        // Find the current tab's main content window.
        var w = gBrowser.selectedBrowser.contentWindow;
        console.log("Tracking allocations in page: " +
                    w.location.href);
    
        // Make that window a debuggee of our Debugger.
        dbg.addDebuggee(w.wrappedJSObject);
    
        // Enable allocation tracking in dbg's debuggees.
        dbg.memory.trackingAllocationSites = true;
      }
    
      window.demoPlotAllocations = function() {
        // Grab the allocation log.
        var log = dbg.memory.drainAllocationsLog();
    
        // Neutralize the Debugger, and drop it on the floor
        // for the GC to collect.
        console.log("Stopping allocation tracking.");
        dbg.removeAllDebuggees();
        dbg = undefined;
    
        // Analyze and display the allocation log.
        plot(log);
      }
    
      function handleUncaughtException(ex) {
        console.log('Debugger hook threw:');
        console.log(ex.toString());
        console.log('Stack:');
        console.log(ex.stack);
      };
    
      function plot(log) {
        // Given the log, compute a map from allocation sites to
        // allocation counts. Note that stack entries are '===' if
        // they represent the same site with the same callers.
        var counts = new Map;
        for (let site of log) {
          // This is a kludge, necessary for now. The saved stacks
          // are new, and Firefox doesn't yet understand that they
          // are safe for chrome code to use, so we must tell it
          // so explicitly.
          site = Components.utils.waiveXrays(site.frame);
    
          if (!counts.has(site))
            counts.set(site, 0);
          counts.set(site, counts.get(site) + 1);
        }
    
        // Walk from each site that allocated something up to the
        // root, computing allocation totals that include
        // children. Remember that 'null' is a valid site,
        // representing the root.
        var totals = new Map;
        for (let [site, count] of counts) {
          for(;;) {
            if (!totals.has(site))
              totals.set(site, 0);
            totals.set(site, totals.get(site) + count);
            if (!site)
              break;
            site = site.parent;
          }
        }
    
        // Compute parent-to-child links, since saved stack frames
        // have only parent links.
        var rootChildren = new Map;
        function childMapFor(site) {
          if (!site)
            return rootChildren;
    
          let parentMap = childMapFor(site.parent);
          if (parentMap.has(site))
            return parentMap.get(site);
    
          var m = new Map;
          parentMap.set(site, m);
          return m;
        }
        for (let [site, total] of totals) {
          childMapFor(site);
        }
    
        // Print the allocation count for |site|. Print
        // |children|'s entries as |site|'s child nodes. Indent
        // the whole thing by |indent|.
        function walk(site, children, indent) {
          var name, place;
          if (site) {
            name = site.functionDisplayName;
            place = '  ' + site.source + ':' + site.line + ':' + site.column;
          } else {
            name = '(root)';
            place = '';
          }
          console.log(indent + totals.get(site) + ': ' + name + place);
          for (let [child, grandchildren] of children)
            walk(child, grandchildren, indent + '   ');
        }
        walk(null, rootChildren, '');
      }
    })();
  4. Dans l'ardoise vérifiez qu'aucun texte n'est sélectioné, et appuyez sur le bouton "Executer". (Si vous avez une erreur disant que Components.utils n'est pas défini, vérifier que vous avez bien séléectioné Navigateur comme décrit dans l'étape 2

  5. Sauvegardez le code HTML suivant dans un fichier et ouvrez celui ci dans le navigateur. Vérifiez que l'onglet ouvert dans votre navigateur affiche bien cette page.

    <div onclick="doDivsAndSpans()">
      Click here to make the page do some allocations.
    </div>
    
    <script>
      function makeFactory(type) {
        return function factory(content) {
          var elt = document.createElement(type);
          elt.textContent = content;
          return elt;
        };
      }
    
      var divFactory = makeFactory('div');
      var spanFactory = makeFactory('span');
    
      function divsAndSpans() {
        for (i = 0; i < 10; i++) {
          var div = divFactory('div #' + i);
          div.appendChild(spanFactory('span #' + i));
          document.body.appendChild(div);
        }
      }
    
      function doDivsAndSpans() { divsAndSpans(); }
    </script>
  6. Ouvrez la console du navigateur  (Menu > Développement > Console du navigateur ), et évaluez l'expression demoTrackAllocations() dans la console du navigateur. Cela commence l'enregistrement des allocations de l'onglet en cours.

  7. Dans l'onglet du navigateur, cliquez sur le texte qui dit "Click here...". Le gestionnaire d'évènement devrait ajouter du texte à la fin de la page.

  8. De retour dans la console du navigateur, évaluez l'expression demoPlotAllocations(). Cela arrête l'enregistrement des allocations, et affiche l'arbre des allocations :

    An allocation plot, displayed in the console

     

    Le nombre à la gauche de chaque ligne représente le nombre total d'objets alloués à cet endroit ou aux endroits appelées depuis cet endroit. Après le nombre, il y a le nom de la fonction, et la position dans le code source.

    Le nombre du noeud root inclut les objets alloués dans le contenu de la page par le navigateur web (les évènements DOM par exemple). Cet affichage affiche en effet que popup.xml et content.js, qui sont des composants internes à Firefox, allouent plus d'objets dans le compartiment de la page que la page elle-même. (Nous modifierons peut-être le message d'allocation pour présenter celles-ci d'une façon qui expose moins la structure interne de Firefox et ainsi serait plus informative.)

    Comme attendu, le onclick est responsable pour toutes les allocations faites par le code de la page. (Le nombre de lignes pour le onclick est de 1, ce qui indique que l'appel d'allocation est situé à la ligne 1 du handler, nous changerons peut être cela pour qu’à la place soit affiché le numéro de ligne dans le fichier)

    Le onclick appelle doDivsAndSpans, qui a son tour appelle divsAndSpans,qui invoque des closures de factory qui font véritablement les allocations. (On ne sait pas pourquoi spanFactory alloue 30 objets, alors qu'il n'est appelé que 10 fois...)

Source Metadata

Généré depuis le fichier :
 
js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md
Watermark:
sha256:22e6cb52070fa7913b9b0070c44f11dfc9c9acfa87a78950b76c0546dad35096
Changeset:
cfb5851c96ea+

Étiquettes et contributeurs liés au document

Étiquettes : 
 Contributeurs à cette page : maximelore
 Dernière mise à jour par : maximelore,