Home | History | Annotate | Download | only in LeaksViewer
      1 /*
      2  * Copyright (C) 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 function LeaksParserWorker() {
     27     this.profile = this._createNode("top level");
     28 }
     29 
     30 LeaksParserWorker.prototype = {
     31     addLeaksFile: function(leaksText) {
     32         this._incorporateLeaks(this._parseLeaks(leaksText));
     33     },
     34 
     35     _parseLeaks: function(text) {
     36         var leaks = [];
     37         var currentSize = 0;
     38         text.split("\n").forEach(function(line) {
     39             var match = /^Leak:.*\ssize=(\d+)\s/.exec(line);
     40             if (match) {
     41                 currentSize = parseInt(match[1], 10);
     42                 return;
     43             }
     44             if (!/^\s+Call stack:/.test(line))
     45                 return;
     46 
     47             // The first frame is not really a frame at all ("Call stack: thread 0xNNNNN:"), so we omit it.
     48             leaks.push({ size: currentSize, stack: line.split(" | ").slice(1).map(function(str) { return str.trim(); }) });
     49             currentSize = 0;
     50         });
     51         return leaks;
     52     },
     53 
     54     _createNode: function(functionName) {
     55         return {
     56             functionName: functionName,
     57             selfTime: 0,
     58             totalTime: 0,
     59             averageTime: 0,
     60             numberOfCalls: 0,
     61             children: [],
     62             childrenByName: {},
     63             callUID: functionName,
     64         };
     65     },
     66 
     67     // This function creates a fake "profile" from a set of leak stacks. "selfTime" is the number of
     68     // stacks in which this function was at the top (in theory, only functions like malloc should have a
     69     // non-zero selfTime). "totalTime" is the number of stacks which contain this function (and thus is
     70     // the number of leaks that occurred in or beneath this function).
     71     // FIXME: This is expensive! Can we parallelize it?
     72     _incorporateLeaks: function(leaks) {
     73         var self = this;
     74         leaks.forEach(function(leak) {
     75             leak.stack.reduce(function(node, frame, index, array) {
     76                 var childNode;
     77                 if (frame in node.childrenByName)
     78                     childNode = node.childrenByName[frame];
     79                 else {
     80                     childNode = self._createNode(frame);
     81                     childNode.head = self.profile;
     82                     node.childrenByName[frame] = childNode;
     83                     node.children.push(childNode);
     84                 }
     85                 if (index === array.length - 1)
     86                     childNode.selfTime += leak.size;
     87                 childNode.totalTime += leak.size;
     88                 ++childNode.numberOfCalls;
     89                 return childNode;
     90             }, self.profile);
     91         });
     92         self.profile.totalTime = self.profile.children.reduce(function(sum, child) { return sum + child.totalTime; }, 0);
     93     },
     94 };
     95 
     96 var parser = new LeaksParserWorker();
     97 
     98 onmessage = function(e) {
     99     parser.addLeaksFile(e.data);
    100     postMessage(parser.profile);
    101 }
    102